Files
qt5-legatus-legacy/Lts/Proxies/HighLevelArchitecture/Guez/ngs_connector.cpp
T
2026-08-13 18:33:10 +02:00

2888 lines
111 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/settings.h>
#include <Common/player_callback.h>
#include <Common/identifierstruct.h>
#include <Common/participantinformation.h>
#include "rpr/rpr_observer_guez_human_cb_notifier.h"
#include "rpr/rpr_observer_guez_groundvehicle_cb_notifier.h"
#include "rpr/rpr_observer_guez_observer_controller_cb_notifier.h"
#include "rpr/rpr_observer_guez_minefield_cb_notifier.h"
#include "rpr/rpr_observer_guez_minefield_breach_cb_notifier.h"
#include "rpr/rpr_observer_guez_fire_support_single_shot_cb_notifier.h"
#include "rpr/rpr_ia_munitiondetonation.h"
#include "rpr/rpr_ia_weaponfire.h"
#include "rpr/rpr_ia_guez_hit.h"
#include "rpr/rpr_ia_guez_init.h"
#include "rpr/rpr_ia_guez_exconengagement.h"
#include "rpr/rpr_ia_guez_bitereport.h"
#include "rpr/rpr_ia_guez_requestbitereport.h"
#include <NGSException.h>
#include <QTimer>
#include <QPolygonF>
#include <QTransform>
#include <QDateTime>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <AreaEffectSimulation/aes_enums.hpp>
#include <SimulatedWorldAPI.h>
#include <EventIdentifierStruct.h>
#include <SpatialStruct.h>
#include <IsPartOfStruct.h>
#include <EnvironmentObjectTypeStruct.h>
#include <BiteResultStruct.h>
#include <IObserverGuezHumanObjClass.h>
#include <IObserverGuezObserverControllerObjClass.h>
#include <IObserverGuezGroundVehicleObjClass.h>
#include <IObserverGuezMineFieldObjClass.h>
#include <IObserverGuezMineFieldBreachObjClass.h>
#include <IObserverMunitionDetonationIntClass.h>
#include <IObserverMunitionDetonationIntInstance.h>
#include <IObserverWeaponFireIntClass.h>
#include <IObserverWeaponFireIntInstance.h>
#include <IObserverGuezHitIntClass.h>
#include <IObserverGuezHitIntInstance.h>
#include <IObserverGuezInitIntClass.h>
#include <IObserverGuezInitIntInstance.h>
#include <IObserverGuezBiteReportIntClass.h>
#include <IObserverGuezRequestBiteReportIntClass.h>
#include <IPlayerGuezHumanObjClass.h>
#include <IPlayerGuezHumanObjInstance.h>
#include <IPlayerGuezObserverControllerObjClass.h>
#include <IPlayerGuezObserverControllerObjInstance.h>
#include <IPlayerGuezGroundVehicleObjClass.h>
#include <IPlayerGuezGroundVehicleObjInstance.h>
#include <IPlayerGuezMineFieldBreachObjClass.h>
#include <IPlayerGuezMineFieldBreachObjInstance.h>
#include <IPlayerGuezMineFieldObjClass.h>
#include <IPlayerGuezMineFieldObjInstance.h>
#include <IPlayerGuezChemicalAgentAreaObjClass.h>
#include <IPlayerGuezChemicalAgentAreaObjInstance.h>
#include <IPlayerGuezNuclearAgentAreaObjClass.h>
#include <IPlayerGuezNuclearAgentAreaObjInstance.h>
#include <IPlayerWeaponFireIntClass.h>
#include <IPlayerWeaponFireIntInstance.h>
#include <IPlayerGuezHitIntClass.h>
#include <IPlayerGuezHitIntInstance.h>
#include <IPlayerMunitionDetonationIntClass.h>
#include <IPlayerMunitionDetonationIntInstance.h>
#include <IPlayerGuezRequestBiteReportIntClass.h>
#include <IPlayerGuezRequestBiteReportIntInstance.h>
#include <IPlayerGuezBiteReportIntClass.h>
#include <IPlayerGuezBiteReportIntInstance.h>
#include <IPlayerGuezInitIntClass.h>
#include <IPlayerGuezInitIntInstance.h>
#include <IPlayerSOM.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;
Settings::Orientation orientation = Settings::spatialStructOrientation();
sss.Orientation.Phi = orientation.phi;
sss.Orientation.Psi = orientation.psi;
sss.Orientation.Theta = orientation.theta;
rpr2::SpatialStruct ss;
ss.DeadReckoningAlgorithm__A__Alternatives.discriminant(rpr2::DeadReckoningAlgorithmEnum8::Static);
ss.DeadReckoningAlgorithm__A__Alternatives.SpatialStatic(sss);
return ss;
}
static inline rpr2::EnvironmentObjectTypeStruct dummyEOS()
{
return {2, 3, 4, 5};
}
//
// 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 ObserverGuezHumanCBNotifier())
, m_observerControllerCallback(new ObserverGuezObserverControllerCBNotifier())
, m_groundVehicleCallback(new ObserverGuezGroundVehicleCBNotifier())
, m_minefieldCallback(new ObserverGuezMineFieldCBNotifier())
, m_minefieldBreachCallback(new ObserverGuezMineFieldBreachCBNotifier())
, m_munitionDetonationCallback(new ObserverMunitionDetonationCBNotifier())
, m_weaponfireCallback(new ObserverWeaponFireCBNotifier())
, m_hitCallback(new ObserverGuezHitCBNotifier())
, m_initCallback(new ObserverGuezInitCBNotifier())
, m_biteReportCallback(new ObserverGuezBiteReportCBNotifier())
, m_requestBiteReportCallback(new ObserverGuezRequestBiteReportCBNotifier())
, m_bInitialized(false)
, m_bCanPublishData(false)
, m_timer(new QTimer(this))
, m_logger(getLogger("NgsConnector"))
{
connect(m_timer, &QTimer::timeout,
this, &NgsConnector::go);
connect(m_playerCallback, &PlayerCallback::firstTick,
this, &NgsConnector::onFirstTickFinished);
// Ground Vehicle
connectObserverGuezGroundVehicleObjectSignals(m_groundVehicleCallback);
// Human
connectObserverGuezHumanObjectSignals(m_humanCallback);
// Observer / Controller
connectObserverGuezObserverControllerObjectSignals(m_observerControllerCallback);
// Mine Field
connectObserverGuezMineFieldObjectSignals(m_minefieldCallback);
// Mine Field Breach
connectObserverGuezMineFieldBreachObjectSignals(m_minefieldBreachCallback);
// Munition Detonation
connect(m_munitionDetonationCallback, &ObserverMunitionDetonationCBNotifier::newMunitionDetonation,
this, &NgsConnector::onNewMunitionDetonation);
// Weapon Fire
connect(m_weaponfireCallback, &ObserverWeaponFireCBNotifier::newWeaponFire,
this, &NgsConnector::onNewWeaponFire);
// Hit
connect(m_hitCallback, &ObserverGuezHitCBNotifier::newGuezHit,
this, &NgsConnector::onNewGuezHit);
// Init
connect(m_initCallback, &ObserverGuezInitCBNotifier::newGuezInit,
this, &NgsConnector::onNewGuezInit);
// Request Bite Report
connect(m_requestBiteReportCallback, &ObserverGuezRequestBiteReportCBNotifier::newGuezRequestBiteReport,
this, &NgsConnector::onNewGuezRequestBiteReport);
// Bite Report
connect(m_biteReportCallback, &ObserverGuezBiteReportCBNotifier::newGuezBiteReport,
this, &NgsConnector::onNewGuezBiteReport);
}
NgsConnector::~NgsConnector()
{
uninitialize();
delete m_playerCallback;
delete m_humanCallback;
delete m_observerControllerCallback;
delete m_groundVehicleCallback;
delete m_minefieldCallback;
delete m_minefieldBreachCallback;
delete m_munitionDetonationCallback;
delete m_weaponfireCallback;
delete m_initCallback;
delete m_hitCallback;
delete m_requestBiteReportCallback;
delete m_biteReportCallback;
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 - Guez Human Objects";
rpr2::IObserverGuezHumanObjClass &humanObjClass = m_somPlayer->getObserverGuezHumanObjClass(*m_humanCallback);
humanObjClass.subscribeAllAttributes();
humanObjClass.subscribe();
INFO_LOG(m_logger) << "Subscribe at NGS - Guez ObserverController Objects";
rpr2::IObserverGuezObserverControllerObjClass &observerControllerObjClass = m_somPlayer->getObserverGuezObserverControllerObjClass(*m_observerControllerCallback);
observerControllerObjClass.subscribeAllAttributes();
observerControllerObjClass.subscribe();
INFO_LOG(m_logger) << "Subscribe at NGS - Guez GroundVehicle Objects";
rpr2::IObserverGuezGroundVehicleObjClass &groundVehicleObjClass = m_somPlayer->getObserverGuezGroundVehicleObjClass(*m_groundVehicleCallback);
groundVehicleObjClass.subscribeAllAttributes();
groundVehicleObjClass.subscribe();
INFO_LOG(m_logger) << "Subscribe at NGS - Guez MineField Objects";
rpr2::IObserverGuezMineFieldObjClass &minefieldObjClass = m_somPlayer->getObserverGuezMineFieldObjClass(*m_minefieldCallback);
minefieldObjClass.subscribeAllAttributes();
minefieldObjClass.subscribe();
INFO_LOG(m_logger) << "Subscribe at NGS - Guez MineField Breach Objects";
rpr2::IObserverGuezMineFieldBreachObjClass &minefieldBreachObjClass = m_somPlayer->getObserverGuezMineFieldBreachObjClass(*m_minefieldBreachCallback);
minefieldBreachObjClass.subscribeAllAttributes();
minefieldBreachObjClass.subscribe();
rpr2::IObserverMunitionDetonationIntClass &md = m_somPlayer->getObserverMunitionDetonationIntClass(*m_munitionDetonationCallback);
md.subscribe();
rpr2::IObserverWeaponFireIntClass &wfic = m_somPlayer->getObserverWeaponFireIntClass(*m_weaponfireCallback);
wfic.subscribe();
rpr2::IObserverGuezInitIntClass &giic = m_somPlayer->getObserverGuezInitIntClass(*m_initCallback);
giic.subscribe();
rpr2::IObserverGuezHitIntClass &ghic = m_somPlayer->getObserverGuezHitIntClass(*m_hitCallback);
ghic.subscribe();
rpr2::IObserverGuezRequestBiteReportIntClass &grbric = m_somPlayer->getObserverGuezRequestBiteReportIntClass(*m_requestBiteReportCallback);
grbric.subscribe();
rpr2::IObserverGuezBiteReportIntClass &gbric = m_somPlayer->getObserverGuezBiteReportIntClass(*m_biteReportCallback);
gbric.subscribe();
m_timer->start();
m_bCanPublishData = true;
}
void NgsConnector::unsubscribeFromNgs()
{
m_bCanPublishData = false;
if (m_somPlayer)
{
INFO_LOG(m_logger) << "Unsubscribe at NGS - Guez Human Objects";
rpr2::IObserverGuezHumanObjClass &humanObjClass = m_somPlayer->getObserverGuezHumanObjClass(*m_humanCallback);
humanObjClass.unsubscribe();
INFO_LOG(m_logger) << "Unsubscribe at NGS - Guez ObserverController Objects";
rpr2::IObserverGuezObserverControllerObjClass &observerControllerObjClass = m_somPlayer->getObserverGuezObserverControllerObjClass(*m_observerControllerCallback);
observerControllerObjClass.unsubscribe();
INFO_LOG(m_logger) << "Unsubscribe at NGS - Guez GroundVehicle Objects";
rpr2::IObserverGuezGroundVehicleObjClass &groundVehicleObjClass = m_somPlayer->getObserverGuezGroundVehicleObjClass(*m_groundVehicleCallback);
groundVehicleObjClass.unsubscribe();
INFO_LOG(m_logger) << "Unsubscribe at NGS - Guez MineField Objects";
rpr2::IObserverGuezMineFieldObjClass &minefieldObjClass = m_somPlayer->getObserverGuezMineFieldObjClass(*m_minefieldCallback);
minefieldObjClass.unsubscribe();
INFO_LOG(m_logger) << "Unsubscribe at NGS - Guez MineField Breach Objects";
rpr2::IObserverGuezMineFieldBreachObjClass &minefieldBreachObjClass = m_somPlayer->getObserverGuezMineFieldBreachObjClass(*m_minefieldBreachCallback);
minefieldBreachObjClass.unsubscribe();
}
for (rpr2::IPlayerBaseEntityObjInstance *player : qAsConst(m_playerMap))
player->deleteObjectInstance(getSimTime());
m_playerMap.clear();
for (rpr2::IPlayerEnvironmentObjectObjInstance *environmentObject : qAsConst(m_environmentObjects))
environmentObject->deleteObjectInstance(getSimTime());
m_environmentObjects.clear();
for (auto it = m_observerPlayerInstances.cbegin(); it != m_observerPlayerInstances.cend(); ++it)
emit virtualParticipantDeleted(it.key());
for (auto it = m_observerEnvironmentObjects.cbegin(); it != m_observerEnvironmentObjects.cend(); ++it)
emit virtualParticipantDeleted(it.key());
m_observerPositions.clear();
m_newObjectsWithoutParticipantIdSet.clear();
m_observerPlayerInstances.clear();
m_observerEnvironmentObjects.clear();
qDeleteAll(m_damageModelMap);
m_damageModelMap.clear();
m_enitityNumberCounter = 1; // restart
m_eventCount = 1;
}
bool NgsConnector::addHuman(const ParticipantInformation &participantData,
const rpr2::TacticalStatusStruct &state)
{
if (m_playerMap.contains(participantData.equipmentNo) || participantData.equipmentNo == 0)
return false;
DEBUG_LOG(getLogger("NgsConnector.Outbound.Startup"))
<< "addHuman("
<< participantData.equipmentNo << ", "
<< participantData.partyId << ", "
<< participantData.typeStruct.toString() << ", "
<< state.TacticalStatus << ")";
rpr2::IPlayerGuezHumanObjClass &player = m_somPlayer->getPlayerGuezHumanObjClass();
rpr2::IPlayerGuezHumanObjInstance *playerInstance = &player.newGuezHumanObjInstance();
DamageModel dm(state);
playerInstance->setDamageState(dm.damageState(-1));
playerInstance->setImmobilized(dm.immobilized(-1));
playerInstance->setFirePowerDisabled(dm.firePowerDisabled(-1));
playerInstance->setTacticalStatus(state);
return addPhysicalEntity(playerInstance, participantData);
}
bool NgsConnector::addObserverController(const ParticipantInformation &participantData)
{
if (m_playerMap.contains(participantData.equipmentNo) || participantData.equipmentNo == 0)
return false;
DEBUG_LOG(getLogger("NgsConnector.Outbound.Startup"))
<< "addObserverController("
<< participantData.equipmentNo << ", "
<< participantData.partyId << ", "
<< participantData.typeStruct.toString() << ")";
rpr2::IPlayerGuezObserverControllerObjClass &player = m_somPlayer->getPlayerGuezObserverControllerObjClass();
rpr2::IPlayerGuezObserverControllerObjInstance *playerInstance = &player.newGuezObserverControllerObjInstance();
return addPhysicalEntity(playerInstance, participantData);
}
bool NgsConnector::addGroundVehicle(const ParticipantInformation &participantData,
const rpr2::TacticalStatusStruct &state)
{
if (m_playerMap.contains(participantData.equipmentNo) || participantData.equipmentNo == 0)
return false;
DEBUG_LOG(getLogger("NgsConnector.Outbound.Startup"))
<< "addGroundVehicle("
<< participantData.equipmentNo << ", "
<< participantData.partyId << ", "
<< participantData.typeStruct.toString() << ", "
<< state.TacticalStatus << ")";
rpr2::IPlayerGuezGroundVehicleObjClass &player = m_somPlayer->getPlayerGuezGroundVehicleObjClass();
rpr2::IPlayerGuezGroundVehicleObjInstance *playerInstance = &player.newGuezGroundVehicleObjInstance();
DamageModel dm(state);
playerInstance->setDamageState(dm.damageState(-1));
playerInstance->setImmobilized(dm.immobilized(-1));
playerInstance->setFirePowerDisabled(dm.firePowerDisabled(-1));
playerInstance->setTacticalStatus(state);
return addPhysicalEntity(playerInstance, participantData);
}
bool NgsConnector::addBaseEntity(rpr2::IPlayerBaseEntityObjInstance *playerInstance,
const ParticipantInformation &participantData)
{
if (m_playerMap.contains(participantData.equipmentNo) || participantData.equipmentNo == 0)
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());
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;
DEBUG_LOG(getLogger("NgsConnector.Outbound.ShutDonw")) << "Participant " << eqNo << " removed.";
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;
DEBUG_LOG(getLogger("NgsConnector.Outbound.Participant")) << "Participant " << eqNo << " changed MARKING to " << designator;
playerInstance->setMarking(toMarkingStruct(designator));
playerInstance->publish(getSimTime());
return true;
}
bool NgsConnector::setParticipantForce(int eqNo, const rpr2::ForceIdentifierEnum8::ET forceIdentifier)
{
rpr2::IPlayerPhysicalEntityObjInstance *playerInstance = dynamic_cast<rpr2::IPlayerPhysicalEntityObjInstance *>(m_playerMap.value(eqNo, nullptr));
if (!playerInstance)
return false;
DEBUG_LOG(getLogger("NgsConnector.Outbound.Participant")) << "Participant " << eqNo << "changed FORCE to " << forceIdentifier;
playerInstance->setForceIdentifier(forceIdentifier);
playerInstance->publish(getSimTime());
return true;
}
// DDS -> HLA
void NgsConnector::sendMunitionDetonation(const ParticipantPosition &pos, int chargeTypeId, rpr2::ForceIdentifierEnum8::ET forceId)
{
DEBUG_LOG(getLogger("NgsConnector.Outbound.MunitionDetonation"))
<< "Munition Detonation: "
<< "Location: " << pos.toString()
<< ", Charge Type: " << TypeStruct(getEtsCharge(chargeTypeId)).toString()
<< ", fired by Force " << forceId << " (Force unused)";
rpr2::IPlayerMunitionDetonationIntClass &icx = m_somPlayer->getPlayerMunitionDetonationIntClass();
rpr2::IPlayerMunitionDetonationIntInstance *icb = &icx.newMunitionDetonationIntInstance();
icb->setDetonationLocation(pos.toWorldLocation());
icb->setDetonationResultCode(rpr2::DetonationResultCodeEnum8::Detonation);
icb->setEventIdentifier(getEventIdentifierStruct());
// Todo: Ask Saab if and how we should encode the Party Id
rpr2::RTIObjectIdStruct os;
Q_UNUSED(forceId);
os.ID = std::to_string(IdentifierStruct(0).toInt64()); // Own ExCon
icb->setFiringObjectIdentifier(os);
icb->setFuseType(rpr2::FuseTypeEnum16::Contact);
icb->setMunitionType(getEtsCharge(chargeTypeId));
icb->setQuantityFired(1);
icb->setRateOfFire(0);
// icb.setTargetObjectIdentifier("");
icb->setWarheadType(rpr2::WarheadTypeEnum16::Kinetic);
icb->publish(getSimTime(pos.msec));
}
IdentifierStruct NgsConnector::getIdentifierStructbyEquipmentNo(const int eqId) const
{
const rpr2::IObserverBaseEntityObjInstance *beObject = nullptr;
if (m_observerPlayerInstances.contains(eqId))
beObject = dynamic_cast<const rpr2::IObserverBaseEntityObjInstance *>(m_observerPlayerInstances[eqId]);
else if (m_playerMap.contains(eqId))
beObject = m_playerMap[eqId];
if (!beObject || !beObject->isValidEntityIdentifier())
return IdentifierStruct();
return IdentifierStruct(beObject->getEntityIdentifier());
}
void NgsConnector::sendParticipantInitRequest(const int eqId, bool resetManipulation, bool resetVulnerabilityState, bool restockCharges, qint64 simTimeMSecs)
{
IdentifierStruct is = getIdentifierStructbyEquipmentNo(eqId);
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 " << eqId << " to CombatReady. Not supported.";
return;
}
// Hack as Saab returns with GuezHit -> Combat Ready, but doesn't change status if not restockCharges is true:
restockCharges = true;
DEBUG_LOG(getLogger("NgsConnector.Outbound.GuezInit"))
<< "Request to "
// << (resetManipulation ? "reset Manipulation" : "")
// << (resetManipulation && restockCharges ? " and " : "")
<< (restockCharges ? "restock Charges" : "")
<< " for Player " << eqId << " to Combat Ready. (Reset Manipulation is implicit for SAAB).";
rpr2::IPlayerGuezInitIntClass &icx = m_somPlayer->getPlayerGuezInitIntClass();
rpr2::IPlayerGuezInitIntInstance *icb = &icx.newGuezInitIntInstance();
icb->setActivateHelmetObservation(rpr2::ActivateHelmetObservationEnum8::NoChange);
icb->setEntityIdentifier(is.toEIS());
icb->setResetPlayer(restockCharges ? rpr2::ResetPlayerEnum8::CombatReadyWithReammunition : rpr2::ResetPlayerEnum8::CombatReadyWithoutReammunition);
icb->publish(getSimTime(simTimeMSecs));
}
void NgsConnector::sendParticipantBiteRequest(const int eqId, bool withSelfTest, qint64 simTimeMsecs)
{
IdentifierStruct is = getIdentifierStructbyEquipmentNo(eqId);
if (is.isNull() || is.isOwnElement())
return;
rpr2::IPlayerGuezRequestBiteReportIntClass &icx = m_somPlayer->getPlayerGuezRequestBiteReportIntClass();
rpr2::IPlayerGuezRequestBiteReportIntInstance *icb = &icx.newGuezRequestBiteReportIntInstance();
icb->setBiteReportType(withSelfTest ? rpr2::BiteReportTypeEnum8::StatusAfterSelftest : rpr2::BiteReportTypeEnum8::CurrentStatus);
icb->setEntityIdentifier(is.toEIS());
icb->publish(getSimTime(simTimeMsecs));
DEBUG_LOG(getLogger("NgsConnector.Outbound.GuezBiteRequest"))
<< "Request for Bite Report for Player " << eqId << " with" << (withSelfTest ? "" : "out") << " selftest.";
}
bool NgsConnector::guezMineField(const AreaEffectSimulation::MineArea &data)
{
IdentifierStruct is = IdentifierStruct::fromInt64(data.externalId());
if (!is.isOwnElement())
return false;
rpr2::IPlayerGuezMineFieldObjClass &ic = m_somPlayer->getPlayerGuezMineFieldObjClass();
const QList<rpr2::IPlayerEnvironmentObjectObjInstance *> icbs = m_environmentObjects.values(data.id());
// Check if it is expired or removed (now), then delete the segments.
if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired ||
data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved)
{
for (rpr2::IPlayerEnvironmentObjectObjInstance *item : icbs)
item->deleteObjectInstance(ngs::SimTime(true));
// And we're done. Don't publish anything if called by startup.
m_environmentObjects.remove(data.id());
return true;
}
if (data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsCreated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPrepared &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsActivated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPaused &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsReceived)
return false;
QVector<rpr2::IPlayerGuezMineFieldObjInstance *> minefields;
minefields.reserve(std::max(icbs.size(), int(data.segments().size())));
for (rpr2::IPlayerEnvironmentObjectObjInstance *item : icbs)
minefields.push_back(dynamic_cast<rpr2::IPlayerGuezMineFieldObjInstance *>(item));
int offset = 0;
// If size increased, add the needed segments.
while (minefields.size() < int(data.segments().size()))
{
rpr2::IPlayerGuezMineFieldObjInstance *icb = &ic.newGuezMineFieldObjInstance();
// Todo: Generate an external ID
is.setEntityNumber(offset++ + 10 * data.id());
icb->setObjectIdentifier(is.toEIS());
icb->setObjectType(dummyEOS());
minefields.push_back(icb);
}
// If size is reduced, remove those that are too many.
while (minefields.size() > int(data.segments().size()))
{
rpr2::IPlayerGuezMineFieldObjInstance *icb = minefields.takeLast();
if (icb)
icb->deleteObjectInstance(ngs::SimTime(true));
DEBUG_LOG(getLogger("NgsConnector.Outbound.MineField"))
<< "Minefield: Id: " << is.toString()
<< ", Name: " << data.name()
<< ", number of segments reduced, removing earlier published segment";
}
rpr2::ForceIdentifierEnum8::ET forceId = data.partyId().is_set() ? m_ngsHandler->ppmHandler()->forceIdentifierFromPartyId(data.partyId().get()) : rpr2::ForceIdentifierEnum8::Other;
m_environmentObjects.remove(data.id());
for (int i = 0; i < minefields.size(); ++i)
{
rpr2::IPlayerGuezMineFieldObjInstance *icb = minefields.at(i);
if (!icb)
continue; // should not happen
const AreaEffectSimulation::MineSegment &segment = data.segments()[i];
icb->setActivationTime(getClockTimeStruct(data.effectStart().is_set() ? data.effectStart().get() : 0));
icb->setDeactivated(data.effectStateId().underlying() != AreaEffectSimulation::AesEffectState_def::EsActivated);
if (data.effectStateId().underlying() == AreaEffectSimulation::AesEffectState_def::EsExpired ||
data.effectStateId().underlying() == AreaEffectSimulation::AesEffectState_def::EsRemoved)
icb->setPercentComplete(100);
else
icb->setPercentComplete(0);
icb->setAreaEffectSimulationName(rpr2::OMT13string(data.name()));
icb->setDuration(data.effectDuration() / 1000 / 60 / 60);
icb->setMineType(getEtsCharge(data.chargeStruct().chargeTypeId()));
icb->setMineCount((data.density() - 0.1) / 0.008);
icb->setForceIdentifier(forceId);
icb->setPointsData(positionArray(mineAreaSegment(segment, data.width(), data.density(), data.isCenterLine())));
icb->setObjectType(dummyEOS());
icb->publish(getSimTime(data.sampleTime()));
DEBUG_LOG(getLogger("NgsConnector.Outbound.MineField"))
<< "Minefield: Id: " << is.toString()
<< ", Name: " << data.name()
<< ". Published Segment.";
m_environmentObjects.insertMulti(data.id(), icb);
}
return true;
}
bool NgsConnector::guezMineFieldBreach(const AreaEffectSimulation::MineCorridor &data)
{
IdentifierStruct is = IdentifierStruct::fromInt64(data.externalId());
if (!is.isOwnElement())
return false;
rpr2::IPlayerGuezMineFieldBreachObjClass &ic = m_somPlayer->getPlayerGuezMineFieldBreachObjClass();
const QList<rpr2::IPlayerEnvironmentObjectObjInstance *> icbs = m_environmentObjects.values(data.id());
// Check if it is expired or removed (now), then delete the segments.
if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired ||
data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved)
{
for (rpr2::IPlayerEnvironmentObjectObjInstance *item : icbs)
item->deleteObjectInstance(ngs::SimTime(true));
m_environmentObjects.remove(data.id());
// And we're done. Don't publish anything if called by startup.
return true;
}
if (data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsCreated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsReceived &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPrepared &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsActivated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPaused)
return false;
QMap<int, rpr2::IPlayerGuezMineFieldBreachObjInstance *> map;
int c = 0;
for (rpr2::IPlayerEnvironmentObjectObjInstance *item : icbs)
map.insert(c++, dynamic_cast<rpr2::IPlayerGuezMineFieldBreachObjInstance *>(item));
// If size increased, add the needed segments.
if (map.size() < int(data.segments().size()))
{
int offset = 0;
for (int i = map.size(); i < int(data.segments().size()); ++i)
{
rpr2::IPlayerGuezMineFieldBreachObjInstance *icb = &ic.newGuezMineFieldBreachObjInstance();
// TODO: why not is.setEntityNumber(offset++ + 10 * data.id()); as in guezMineField() ?
const IdentifierStruct isSegment(offset++ + 10 * data.id());
icb->setObjectIdentifier(isSegment.toEIS());
map.insert(i, icb);
}
}
// If size is reduced, remove those that are too many.
for (int i = int(data.segments().size()); i < map.size(); ++i)
{
rpr2::IPlayerGuezMineFieldBreachObjInstance *icb = map.value(i);
if (icb)
icb->deleteObjectInstance(ngs::SimTime(true));
DEBUG_LOG(getLogger("NgsConnector.Outbound.MineFieldBreach"))
<< "Minefield Breach: Id: " << is.toString()
<< ", Name: " << data.name()
<< ", number of segments reduced, removing earlier published segment";
map.remove(i);
}
m_environmentObjects.remove(data.id());
for (int i = 0; i < map.size() - 1; ++i)
{
rpr2::IPlayerGuezMineFieldBreachObjInstance *icb = map.value(i);
if (!icb)
continue; // should not happen
icb->setActivationTime(getClockTimeStruct(data.effectStart().is_set() ? data.effectStart().get() : 0));
icb->setAreaEffectSimulationName(rpr2::OMT13string(data.name()));
icb->setPointsData(positionArray(mineCorridorSegment(data.segments().at(i), data.segments().at(i + 1), data.width())));
icb->setDeactivated(data.effectStateId().underlying() != AreaEffectSimulation::AesEffectState_def::EsActivated);
if (data.effectStateId().underlying() == AreaEffectSimulation::AesEffectState_def::EsExpired ||
data.effectStateId().underlying() == AreaEffectSimulation::AesEffectState_def::EsRemoved)
icb->setPercentComplete(100);
else
icb->setPercentComplete(0);
icb->setObjectType(dummyEOS());
icb->publish(getSimTime(data.sampleTime()));
DEBUG_LOG(getLogger("NgsConnector.Outbound.MineFieldBreach"))
<< "Minefield Breach: Id: " << is.toString()
<< ", Name: " << data.name()
<< ". Published Segment.";
m_environmentObjects.insertMulti(data.id(), icb);
}
return true;
}
bool NgsConnector::guezNuclearAgent(const AreaEffectSimulation::NContamination &data)
{
IdentifierStruct is = IdentifierStruct::fromInt64(data.externalId());
if (!is.isOwnElement())
return false;
rpr2::IPlayerGuezNuclearAgentAreaObjClass &ic = m_somPlayer->getPlayerGuezNuclearAgentAreaObjClass();
rpr2::IPlayerGuezNuclearAgentAreaObjInstance *icb = dynamic_cast<rpr2::IPlayerGuezNuclearAgentAreaObjInstance *>(m_environmentObjects.value(data.id()));
// Check if it is expired or removed (now), then delete the segments.
if (icb &&
(data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired ||
data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved))
{
icb->deleteObjectInstance(ngs::SimTime(true));
m_environmentObjects.remove(data.id());
// And we're done. Don't publish anything if called by startup.
return true;
}
if (data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsCreated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsReceived &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPrepared &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsActivated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPaused)
return false;
if (!icb)
icb = &ic.newGuezNuclearAgentAreaObjInstance();
ParticipantPosition pos;
pos.lon = data.impactPosition().lon();
pos.lat = data.impactPosition().lat();
// Todo: Generate an external ID
is.setEntityNumber(10 * data.id());
icb->setObjectIdentifier(is.toEIS());
icb->setActivationTime(getClockTimeStruct(data.effectStart().is_set() ? data.effectStart().get() : 0));
icb->setAreaEffectSimulationName(rpr2::OMT13string(data.name()));
icb->setDuration(data.effectDuration());
icb->setDetonationLocation(pos.toWorldLocation());
icb->setPointsData(positionArray(nContamination(data)));
icb->setRadius(data.impactRadius());
icb->setObjectType(dummyEOS());
icb->publish(getSimTime(data.sampleTime()));
m_environmentObjects.insert(data.id(), icb);
DEBUG_LOG(getLogger("NgsConnector.Outbound.NuclearAgentArea"))
<< "Nuclear Agent Area: Id: " << is.toString()
<< ", Name: " << data.name()
<< ". Published Area.";
return true;
}
bool NgsConnector::guezChemicalAgent(const AreaEffectSimulation::BCContamination &data)
{
IdentifierStruct is = IdentifierStruct::fromInt64(data.externalId());
if (!is.isOwnElement())
return false;
rpr2::IPlayerGuezChemicalAgentAreaObjClass &ic = m_somPlayer->getPlayerGuezChemicalAgentAreaObjClass();
rpr2::IPlayerGuezChemicalAgentAreaObjInstance *icb = dynamic_cast<rpr2::IPlayerGuezChemicalAgentAreaObjInstance *>(m_environmentObjects.value(data.id()));
// Check if it is expired or removed (now), then delete the item.
if (icb &&
(data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired ||
data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved))
{
icb->deleteObjectInstance(ngs::SimTime(true));
m_environmentObjects.remove(data.id());
// And we're done. Don't publish anything if called by startup.
return true;
}
if (data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsCreated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsReceived &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPrepared &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsActivated &&
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPaused)
return false;
if (!icb)
icb = &ic.newGuezChemicalAgentAreaObjInstance();
ParticipantPosition pos;
pos.lon = data.impactPosition().lon();
pos.lat = data.impactPosition().lat();
// Better: Don't use 400x, obtain volatility type from chargeType-Topic
rpr2::ChemicalAgentTypeEnum8::ET type = rpr2::ChemicalAgentTypeEnum8::Other;
switch (data.chargeStruct().chargeTypeId())
{
case 341:
type = rpr2::ChemicalAgentTypeEnum8::Sarin;
break;
case 342:
type = rpr2::ChemicalAgentTypeEnum8::VX;
break;
case 343:
type = rpr2::ChemicalAgentTypeEnum8::SLost;
break;
default:
type = rpr2::ChemicalAgentTypeEnum8::Other;
}
is.setEntityNumber(10 * data.id());
icb->setObjectIdentifier(is.toEIS());
icb->setDeactivated(data.effectStateId().underlying() != AreaEffectSimulation::AesEffectState_def::EsActivated);
icb->setActivationTime(getClockTimeStruct(data.effectStart().is_set() ? data.effectStart().get() : 0));
icb->setAreaEffectSimulationName(rpr2::OMT13string(data.name()));
icb->setDuration(data.effectDuration() / 1000 / 60); // Duration in Minutes...
icb->setDetonationLocation(pos.toWorldLocation());
if (data.effectStateId().underlying() == AreaEffectSimulation::AesEffectState_def::EsExpired ||
data.effectStateId().underlying() == AreaEffectSimulation::AesEffectState_def::EsRemoved)
icb->setPercentComplete(100);
else
icb->setPercentComplete(0);
// PointsData will be set by AesItemViewChanged()
icb->setChemicalAgentType(type);
icb->setRadius(data.impactRadius());
rpr2::ForceIdentifierEnum8::ET forceId = data.partyId().is_set() ? m_ngsHandler->ppmHandler()->forceIdentifierFromPartyId(data.partyId().get()) : rpr2::ForceIdentifierEnum8::Other;
icb->setForceIdentifier(forceId);
icb->setIsPermanentAgent(type != rpr2::ChemicalAgentTypeEnum8::Sarin);
icb->setObjectType(dummyEOS());
icb->publish(getSimTime(data.sampleTime()));
m_environmentObjects.insert(data.id(), icb);
DEBUG_LOG(getLogger("NgsConnector.Outbound.ChemicalAgentArea"))
<< "Chemical Agent Area: Id: " << is.toString()
<< ", Name: " << data.name()
<< ". Published Area.";
return true;
}
bool NgsConnector::setItemForm(int aesItemId, int aesTypeId, const std::vector<Core::GeoPosition> &form)
{
if (form.size() == 0)
m_arealObjectForms.remove(aesItemId);
else
m_arealObjectForms.insert(aesItemId, form);
if (!m_environmentObjects.contains(aesItemId))
return true;
std::vector<Core::GeoPosition> reducedForm;
switch (aesTypeId)
{
case AesType_def::AtBCContamination:
reducedForm = bcReduction(form);
break;
case AesType_def::AtMineCorridor:
case AesType_def::AtExplosionArea:
case AesType_def::AtExplosionSingle:
case AesType_def::AtMineArea:
case AesType_def::AtNContamination:
// Change only occurs when changing base data.
return true;
case AesType_def::AtUnknown:
case AesType_def::AtDangerArea:
case AesType_def::AtFunctionalArea:
case AesType_def::AtBorderline:
case AesType_def::AtTechnicalArea:
case AesType_def::AtReconArea:
case AesType_def::AtDusSingleMine:
case AesType_def::AtExplosionAreaSingleExplosions:
case AesType_def::AtMineAreaSingleMines:
case AesType_def::AtGasPipe:
// Not published.
return true;
}
rpr2::IPlayerArealObjectObjInstance *ao = dynamic_cast<rpr2::IPlayerArealObjectObjInstance *>(m_environmentObjects.value(aesItemId));
if (!ao)
return false;
ao->setPointsData(positionArray(reducedForm));
ao->publish(getSimTime()); // No time given, take current time
// Note: Case returns whenever Type is NOT Chemical Agent. If that changes, we have to update the logger channel
DEBUG_LOG(getLogger("NgsConnector.Outbound.ChemicalAgentArea"))
<< "Chemical Agent Area: Id: " << aesItemId
<< ". Shape Changed. Updated on HLA.";
return true;
}
bool NgsConnector::setWorldLocation(int eqNo, const ParticipantPosition &pos, int eqNoHost)
{
rpr2::IPlayerPhysicalEntityObjInstance *playerInstance = dynamic_cast<rpr2::IPlayerPhysicalEntityObjInstance *>(m_playerMap.value(eqNo, nullptr));
if (!playerInstance)
return false;
rpr2::SpatialStruct ss(spatialStructFromWorldLocation(pos.toWorldLocation()));
rpr2::ConstituentPartRelationshipStruct cprs;
cprs.Nature = rpr2::ConstituentPartNatureEnum16::Other;
cprs.Position = rpr2::ConstituentPartPositionEnum16::Inside;
rpr2::RTIObjectIdStruct rtiObjectIdHost;
rpr2::RelativeRangeBearingStruct rrbs;
rrbs.Bearing = 0.0;
rrbs.Range = 0.0;
// NGS Codegen does not automatically fill structure with 8 Bytes, when NamedLocationStruct is not defined with
// a valid alternative:
rpr2::NamedLocationStruct__StationName nlssn(rpr2::ConstituentPartStationNameEnum16::OnStationRangeBearing);
nlssn.RelativeRangeAndBearing(rrbs);
rpr2::NamedLocationStruct nlStruct;
nlStruct.StationNumber = 0;
nlStruct.StationName__A__Alternatives = nlssn;
rpr2::EntityIdentifierStruct eis;
eis.EntityNumber = 0;
eis.FederateIdentifier.ApplicationID = 0;
eis.FederateIdentifier.SiteID = 0;
// Mounted?
if (eqNoHost > 0)
{
// Get host from our players
const rpr2::IObserverBaseEntityObjInstance *observerInstanceHost = m_playerMap.value(eqNoHost);
// Not ours? Get from their players (our observed ones)
if (!observerInstanceHost)
observerInstanceHost = dynamic_cast<const rpr2::IObserverBaseEntityObjInstance *>(m_observerPlayerInstances.value(eqNoHost));
// If found and valid data, fill IsPartOf struct and apply to player
if (observerInstanceHost && observerInstanceHost->isValidObjectName() && observerInstanceHost->isValidEntityIdentifier())
{
cprs.Position = rpr2::ConstituentPartPositionEnum16::Inside;
rtiObjectIdHost.ID = observerInstanceHost->getObjectName();
eis = observerInstanceHost->getEntityIdentifier();
}
}
rpr2::IsPartOfStruct ipos;
ipos.HostEntityIdentifier = eis;
ipos.HostRTIObjectIdentifier = rtiObjectIdHost;
ipos.NamedLocation = nlStruct;
ipos.Relationship = cprs;
playerInstance->setIsPartOf(ipos);
playerInstance->setSpatial(ss);
playerInstance->publish(getSimTime(pos.msec));
DEBUG_LOG(getLogger("NgsConnector.Outbound.Player.Position"))
<< "Player: " << eqNo << ", Position: " << pos.toString() << ", Time: " << pos.msec << ", Mounted on: " << eqNoHost;
DEBUG_LOG(getLogger("NgsConnector.Outbound.Player.Orientation"))
<< "Player: " << eqNo << ", Orientation(Phi, Psi, Theta): "
<< ss.DeadReckoningAlgorithm__A__Alternatives.SpatialStatic().Orientation.Phi << ", "
<< ss.DeadReckoningAlgorithm__A__Alternatives.SpatialStatic().Orientation.Psi << ", "
<< ss.DeadReckoningAlgorithm__A__Alternatives.SpatialStatic().Orientation.Theta;
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::onEntityIdentifierChanged, Qt::UniqueConnection);
connect(entity, &ObserverBaseEntityObjectCB::spatialChanged,
this, &NgsConnector::onSpatialChanged, Qt::UniqueConnection);
connect(entity, &ObserverBaseEntityObjectCB::entityTypeChanged,
this, &NgsConnector::onEntityTypeChanged, Qt::UniqueConnection);
connect(entity, &ObserverBaseEntityObjectCB::isPartOfChanged,
this, &NgsConnector::onIsPartOfChanged, 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);
}
void NgsConnector::connectObserverPlatformObjectSignals(ObserverPlatformObjectCB *entity)
{
connectObserverPhysicalEntityObjectSignals(entity);
// No extra signals
}
void NgsConnector::connectObserverGroundVehicleObjectSignals(ObserverGroundVehicleObjectCB *entity)
{
connectObserverPlatformObjectSignals(entity);
// No extra signals
}
void NgsConnector::connectObserverGuezGroundVehicleObjectSignals(ObserverGuezGroundVehicleObjectCB *entity)
{
connectObserverGroundVehicleObjectSignals(entity);
connect(entity, &ObserverGuezGroundVehicleObjectCB::tacticalStatusChanged,
this, &NgsConnector::onTacticalStatusChanged, Qt::UniqueConnection);
}
void NgsConnector::connectObserverLifeformObjectSignals(ObserverLifeformObjectCB *entity)
{
connectObserverPhysicalEntityObjectSignals(entity);
// No extra signals
}
void NgsConnector::connectObserverHumanObjectSignals(ObserverHumanObjectCB *entity)
{
connectObserverLifeformObjectSignals(entity);
// No extra signals
}
void NgsConnector::connectObserverGuezHumanObjectSignals(ObserverGuezHumanObjectCB *entity)
{
connectObserverHumanObjectSignals(entity);
connect(entity, &ObserverGuezHumanObjectCB::tacticalStatusChanged,
this, &NgsConnector::onTacticalStatusChanged, Qt::UniqueConnection);
}
void NgsConnector::connectObserverGuezObserverControllerObjectSignals(ObserverGuezObserverControllerObjectCB *entity)
{
connectObserverHumanObjectSignals(entity);
// No extra signals
}
void NgsConnector::connectObserverEnvironmentObjectObjectSignals(ObserverEnvironmentObjectObjectCB *entity)
{
connectObserverObjectSignals(entity);
connect(entity, &ObserverEnvironmentObjectObjectCB::objectIdentifierChanged,
this, &NgsConnector::onObjectIdentifierChanged, Qt::UniqueConnection);
connect(entity, &ObserverEnvironmentObjectObjectCB::forceIdentifierChangedEO,
this, &NgsConnector::onForceIdentifierChangedEO, Qt::UniqueConnection);
connect(entity, &ObserverEnvironmentObjectObjectCB::spatialChanged,
this, &NgsConnector::onSpatialChanged, Qt::UniqueConnection);
}
void NgsConnector::connectObserverArealObjectObjectSignals(ObserverArealObjectObjectCB *entity)
{
connectObserverEnvironmentObjectObjectSignals(entity);
connect(entity, &ObserverArealObjectObjectCB::pointsDataChanged,
this, &NgsConnector::onPointsDataChanged, Qt::UniqueConnection);
connect(entity, &ObserverArealObjectObjectCB::percentCompleteChanged,
this, &NgsConnector::onPercentCompleteChanged, Qt::UniqueConnection);
connect(entity, &ObserverArealObjectObjectCB::deactivatedChanged,
this, &NgsConnector::onDeactivatedChanged, Qt::UniqueConnection);
}
void NgsConnector::connectObserverPointObjectObjectSignals(ObserverPointObjectObjectCB *entity)
{
connectObserverEnvironmentObjectObjectSignals(entity);
}
void NgsConnector::connectObserverOtherArealObjectObjectSignals(ObserverOtherArealObjectObjectCB *entity)
{
connectObserverArealObjectObjectSignals(entity);
}
void NgsConnector::connectObserverOtherPointObjectObjectSignals(ObserverOtherPointObjectObjectCB *entity)
{
connectObserverPointObjectObjectSignals(entity);
}
void NgsConnector::connectObserverGuezMineFieldObjectSignals(ObserverGuezMineFieldObjectCB *entity)
{
connectObserverOtherArealObjectObjectSignals(entity);
connect(entity, &ObserverGuezMineFieldObjectCB::areaEffectSimulationNameChanged,
this, &NgsConnector::onAreaEffectSimulationNameChanged, Qt::UniqueConnection);
connect(entity, &ObserverGuezMineFieldObjectCB::activationTimeChanged,
this, &NgsConnector::onActivationTimeChanged, Qt::UniqueConnection);
connect(entity, &ObserverGuezMineFieldObjectCB::durationChanged,
this, &NgsConnector::onDurationChanged, Qt::UniqueConnection);
connect(entity, &ObserverGuezMineFieldObjectCB::mineTypeChanged,
this, &NgsConnector::onMineTypeChanged, Qt::UniqueConnection);
}
void NgsConnector::connectObserverGuezMineFieldBreachObjectSignals(ObserverGuezMineFieldBreachObjectCB *entity)
{
connectObserverOtherArealObjectObjectSignals(entity);
connect(entity, &ObserverGuezMineFieldBreachObjectCB::areaEffectSimulationNameChanged,
this, &NgsConnector::onAreaEffectSimulationNameChanged, Qt::UniqueConnection);
connect(entity, &ObserverGuezMineFieldBreachObjectCB::activationTimeChanged,
this, &NgsConnector::onActivationTimeChanged, 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_newObjectsWithoutParticipantIdSet.insert(&obj);
onEntityIdentifierChanged(beObject->getEntityIdentifier(), beObject);
}
const rpr2::IObserverEnvironmentObjectObjInstance *eoObject = dynamic_cast<const rpr2::IObserverEnvironmentObjectObjInstance *>(&obj);
if (eoObject && eoObject->isValidObjectIdentifier())
{
IdentifierStruct is(eoObject->getObjectIdentifier());
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " added.";
m_newObjectsWithoutParticipantIdSet.insert(&obj);
onObjectIdentifierChanged(eoObject->getObjectIdentifier(), eoObject);
}
}
void NgsConnector::removeObserverObjectInstance(const ngs::IObserverObjectInstance &obj)
{
const rpr2::IObserverBaseEntityObjInstance *beObject = dynamic_cast<const rpr2::IObserverBaseEntityObjInstance *>(&obj);
const rpr2::IObserverEnvironmentObjectObjInstance *eoObject = dynamic_cast<const rpr2::IObserverEnvironmentObjectObjInstance *>(&obj);
IdentifierStruct is;
if (beObject && beObject->isValidEntityIdentifier())
{
is = IdentifierStruct(beObject->getEntityIdentifier());
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player")) << "Player: " << is.toString() << " removed.";
}
else if (eoObject && eoObject->isValidObjectIdentifier())
{
is = IdentifierStruct(eoObject->getObjectIdentifier());
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object")) << "Object: " << is.toString() << " removed.";
}
else
return;
if (is.isOwnElement())
{
// TODO: handle signal
emit virtualParticipantDeleted(is.entityNumber());
}
// really neccessary?
//int *ptr = static_cast<int *>(obj.getUserObjectID());
//delete[] ptr;
m_newObjectsWithoutParticipantIdSet.remove(&obj);
if (beObject)
{
m_observerPlayerInstances.remove(is.entityNumber());
auto it = m_damageModelMap.find(is.entityNumber());
if (it != m_damageModelMap.end())
{
delete it.value();
m_damageModelMap.erase(it);
}
}
if (eoObject)
{
m_observerEnvironmentObjects.remove(is.entityNumber());
emit aesRemoveItem(is.toInt64());
}
}
void NgsConnector::ensureDamageModelExists(const IdentifierStruct &is)
{
if (m_damageModelMap.contains(is.entityNumber()))
return;
DamageModel *dmgModel(new DamageModel(is));
// connect(dmgModel, &DamageModel::damageStateChanged,
// this, &NgsConnector::onDamageStateChangedTimeout);
connect(dmgModel, &DamageModel::playerManipulationChanged,
this, &NgsConnector::playerManipulationChanged);
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;
// New:
// cts.Hours = msecsSinceEpoch / (1000LL * 60 * 60);
// qint64 msecsAfterTheHour = msecsSinceEpoch - (cts.Hours * 1000 * 60 * 60);
// double portionOfAnHour = msecsAfterTheHour / (1000 * 60 * 60);
// cts.TimePastTheHour = lrint(portionOfAnHour * (2LL<<31);
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
// New:
// time += 1000LL * 60 * 60 * (cts.TimePastTheHour / (2LL<<31)) ;
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("2.0.0.0.0.0.1")).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;
}
QPolygonF NgsConnector::mineAreaSegment(const AreaEffectSimulation::MineSegment &segment, double width, double density, bool bIsCenterline)
{
if (qFuzzyIsNull(density))
return QPolygonF();
LtsCoordTrans::UtmCoordinate startPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(segment.positionStart().lon(), segment.positionStart().lat());
LtsCoordTrans::UtmCoordinate endPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(segment.positionEnd().lon(), segment.positionEnd().lat(), startPositionUtm.zone);
const QLineF line({startPositionUtm.x, startPositionUtm.y}, {endPositionUtm.x, endPositionUtm.y});
const auto yOfs1 = bIsCenterline ? (width / 2) : -width;
const auto yOfs2 = bIsCenterline ? -(width / 2) : 0;
QPolygonF polyUtm = QPolygonF({QPointF(0, yOfs2),
QPointF(0, yOfs1),
QPointF(line.length(), yOfs1),
QPointF(line.length(), yOfs2)
});
// Compass, not mathematical!
polyUtm = fromRotation(0.0 - line.angle()).map(polyUtm);
const QPolygonF poly(utmToGeogrphic(polyUtm, startPositionUtm));
return poly;
}
QPolygonF NgsConnector::mineCorridorSegment(const Core::GeoPosition2D &start, const Core::GeoPosition2D &end, double width)
{
QPointF startPoint(start.lon(), start.lat());
QPointF endPoint(end.lon(), end.lat());
LtsCoordTrans::UtmCoordinate startPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(startPoint);
LtsCoordTrans::UtmCoordinate endPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(endPoint, startPositionUtm.zone);
QLineF lengthVector(QPointF(startPositionUtm.x, startPositionUtm.y),
QPointF(endPositionUtm.x, endPositionUtm.y));
QPolygonF segment = QPolygonF({QPointF(0, width),
QPointF(lengthVector.length(), width),
QPointF(lengthVector.length(), 0),
QPointF(0, 0)
});
segment.translate(QPointF(0, -(width / 2)));
segment = fromRotation(0 - lengthVector.angle()).map(segment);
return utmToGeogrphic(segment, startPositionUtm);
}
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::nContamination(const AreaEffectSimulation::NContamination &awf)
{
// Special Case: BorderLengthInner == 0
LtsCoordTrans::UtmCoordinate impactPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(awf.impactPosition().lon(), awf.impactPosition().lat());
if (qFuzzyIsNull(awf.borderLengthInner()))
{
if (!qFuzzyIsNull(awf.borderLengthOuter()))
return QPolygonF();
QPolygonF poly;
poly.reserve(8);
for (int i = 0; i < 360; i += 45)
{
double angle = compass2rad(double(i));
poly << impactPositionUtm.geographic(QPointF(cos(angle) * awf.impactRadius(), sin(angle) * awf.impactRadius()));
}
return poly;
}
if (awf.impactRadius() > awf.borderLengthInner())
return QPolygonF();
double halfSpanAngleRad = deg2rad(awf.spanAngle()) / 2.0; // Angle between MCC and MCT at C
double acos_val = awf.impactRadius() / awf.borderLengthInner(); // Adjacent / Hypotenuse (Angle between SCT and MCT at C)
double scAngleRad = acos(acos_val) + halfSpanAngleRad; // Thus, Angle between MCC and SCT at C. ("Small Circle Angle")
// Calculate Points:
QPointF sct(cos(scAngleRad) * awf.impactRadius(), sin(scAngleRad) * awf.impactRadius());
QPointF scb(cos(scAngleRad) * awf.impactRadius(), -sin(scAngleRad) * awf.impactRadius());
QPointF mct(cos(halfSpanAngleRad) * (awf.borderLengthInner()), sin(halfSpanAngleRad) * (awf.borderLengthInner()));
QPointF mcb(cos(halfSpanAngleRad) * (awf.borderLengthInner()), -sin(halfSpanAngleRad) * (awf.borderLengthInner()));
QPointF bct(cos(halfSpanAngleRad) * (awf.borderLengthInner() + awf.borderLengthOuter()),
sin(halfSpanAngleRad) * (awf.borderLengthInner() + awf.borderLengthOuter()));
QPointF bcb(cos(halfSpanAngleRad) * (awf.borderLengthInner() + awf.borderLengthOuter()),
-sin(halfSpanAngleRad) * (awf.borderLengthInner() + awf.borderLengthOuter()));
QPolygonF polyUtm;
polyUtm.reserve(8);
polyUtm << sct;
polyUtm << mct;
polyUtm << bct; // In For
polyUtm << QPointF(awf.borderLengthInner() + awf.borderLengthOuter(), 0);
polyUtm << bcb; // May not be in for, if (spanAngle % steps != 0)
polyUtm << mcb;
polyUtm << scb; // In For
polyUtm << QPointF(-awf.impactRadius(), 0);
polyUtm = fromRotation(90.0 - awf.rotationAngle()).map(polyUtm); // The cunstructed poly ist heading eastwards but must be rotated as from north
const QPolygonF poly(utmToGeogrphic(polyUtm, impactPositionUtm));
return poly;
}
std::vector<Core::GeoPosition> NgsConnector::bcReduction(const std::vector<Core::GeoPosition> &form)
{
std::vector<Core::GeoPosition> vec;
vec.resize(8);
if (form.size() == 72) // 0, 45, 90, 135, 180, 225, 270, 315° (Kreis!)
{
vec[0] = form[0];
vec[1] = form[9];
vec[2] = form[18];
vec[3] = form[27];
vec[4] = form[36];
vec[5] = form[45];
vec[6] = form[54];
vec[7] = form[63];
}
else if (form.size() == 74) // 0, 60, 120, 180° (rechts), 180, 240, 300, 360° (links) (Wurscht!)
{
vec[0] = form[0];
vec[1] = form[12];
vec[2] = form[24];
vec[3] = form[36];
vec[4] = form[37];
vec[5] = form[49];
vec[6] = form[61];
vec[7] = form[73];
}
return vec;
}
rpr2::ArrayOfHLAoctetN_slsh_AperfectalwaysN_slsh_ADynamicHLApaddingTo32Array NgsConnector::triggeredImpactsArray(const std::vector<uint8_t> &data)
{
rpr2::ArrayOfHLAoctetN_slsh_AperfectalwaysN_slsh_ADynamicHLApaddingTo32Array impacts;
if (data.size() == 0)
return impacts;
impacts.resize(data.size());
unsigned int i = 0;
for (const auto &item : data)
impacts[i++] = static_cast<ngs::CHAR8>(item);
return impacts;
}
rpr2::HitResultEnum16::ET NgsConnector::getHitResultEnum(const ProjectDataManagement::VulnerabilityState &ts)
{
switch (ts.underlying())
{
case ProjectDataManagement::VulnerabilityState_def::CombatReady:
return rpr2::HitResultEnum16::Reset;
case ProjectDataManagement::VulnerabilityState_def::LightDamage:
return rpr2::HitResultEnum16::LightDamage;
case ProjectDataManagement::VulnerabilityState_def::MediumDamage:
return rpr2::HitResultEnum16::MediumDamage;
case ProjectDataManagement::VulnerabilityState_def::HeavyDamage:
return rpr2::HitResultEnum16::HeavyDamage;
case ProjectDataManagement::VulnerabilityState_def::MobilityKill:
return rpr2::HitResultEnum16::MobilityKill;
case ProjectDataManagement::VulnerabilityState_def::VisibleMobilityKill:
return rpr2::HitResultEnum16::MobilityKillVisible;
case ProjectDataManagement::VulnerabilityState_def::WeaponSystemDamage:
return rpr2::HitResultEnum16::WeaponDamage;
case ProjectDataManagement::VulnerabilityState_def::VisibleWeaponSystemDamage:
return rpr2::HitResultEnum16::WeaponDamageVisible;
case ProjectDataManagement::VulnerabilityState_def::CatastrophicKill:
return rpr2::HitResultEnum16::TotalKill;
case ProjectDataManagement::VulnerabilityState_def::TemporarySeverance:
return rpr2::HitResultEnum16::TemporaryFailure;
case ProjectDataManagement::VulnerabilityState_def::NearMiss:
return rpr2::HitResultEnum16::NearMiss;
case ProjectDataManagement::VulnerabilityState_def::HitWithNoEffect:
return rpr2::HitResultEnum16::HitWithNoEffect;
case ProjectDataManagement::VulnerabilityState_def::NContamination:
case ProjectDataManagement::VulnerabilityState_def::BContamination:
case ProjectDataManagement::VulnerabilityState_def::CContamination:
return rpr2::HitResultEnum16::Contaminated;
default:
return rpr2::HitResultEnum16::Reset;
}
return rpr2::HitResultEnum16::Reset;
}
ProjectDataManagement::VulnerabilityState NgsConnector::getTacticalStatusEnum(const rpr2::HitResultEnum16::ET &hr)
{
switch (hr)
{
case rpr2::HitResultEnum16::Reset:
return ProjectDataManagement::VulnerabilityState_def::CombatReady;
case rpr2::HitResultEnum16::LightDamage:
return ProjectDataManagement::VulnerabilityState_def::LightDamage;
case rpr2::HitResultEnum16::MediumDamage:
return ProjectDataManagement::VulnerabilityState_def::MediumDamage;
case rpr2::HitResultEnum16::HeavyDamage:
return ProjectDataManagement::VulnerabilityState_def::HeavyDamage;
case rpr2::HitResultEnum16::MobilityKill:
return ProjectDataManagement::VulnerabilityState_def::MobilityKill;
case rpr2::HitResultEnum16::MobilityKillVisible:
return ProjectDataManagement::VulnerabilityState_def::VisibleMobilityKill;
case rpr2::HitResultEnum16::WeaponDamage:
return ProjectDataManagement::VulnerabilityState_def::WeaponSystemDamage;
case rpr2::HitResultEnum16::WeaponDamageVisible:
return ProjectDataManagement::VulnerabilityState_def::VisibleWeaponSystemDamage;
case rpr2::HitResultEnum16::TotalKill:
return ProjectDataManagement::VulnerabilityState_def::CatastrophicKill;
case rpr2::HitResultEnum16::CheatingKill:
return ProjectDataManagement::VulnerabilityState_def::CatastrophicKill;
case rpr2::HitResultEnum16::NearMiss:
return ProjectDataManagement::VulnerabilityState_def::NearMiss;
case rpr2::HitResultEnum16::HitWithNoEffect:
return ProjectDataManagement::VulnerabilityState_def::HitWithNoEffect;
case rpr2::HitResultEnum16::Contaminated:
return ProjectDataManagement::VulnerabilityState_def::CContamination;
case rpr2::HitResultEnum16::TemporaryFailure:
return ProjectDataManagement::VulnerabilityState_def::TemporarySeverance;
default:
return ProjectDataManagement::VulnerabilityState_def::NoChange;
}
return ProjectDataManagement::VulnerabilityState_def::NoChange;
}
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::onEntityIdentifierChanged(const rpr2::EntityIdentifierStruct &eis,
const rpr2::IObserverBaseEntityObjInstance *obj)
{
IdentifierStruct is(eis);
// Own players are irrelevant here
if (is.isOwnElement())
{
m_newObjectsWithoutParticipantIdSet.remove(obj);
return;
}
// We cannot do anything here then. Who was it before?
if (m_observerPlayerInstances.contains(is.entityNumber()))
return;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player"))
<< "Player: " << is.toString() << " will be added.";
m_observerPlayerInstances.insert(is.entityNumber(), obj);
m_newObjectsWithoutParticipantIdSet.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]() 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.";
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);
long time = obj->getLatestTimeStamp().getSeconds() * 1000;
if (time == 0)
time = getSimTime().getSeconds() * 1000;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.Position"))
<< "Player: " << hs.targetId << ", Initial Position: " << pos.toString() << ", Time: " << pos.msec;
emit spatialChanged(hs.targetId, pos, time);
// That may be too much but better safe than sorry...
// TODO: set to DamageModel, use emit from there?
emit playerManipulationChanged(hs.targetId, vulnerabilityBits & rpr2::TotalStatusEnum32::Manipulation, hs.sampleTime);
});
}
bool NgsConnector::onObjectIdentifierChanged(const rpr2::EntityIdentifierStruct &eis,
const rpr2::IObserverEnvironmentObjectObjInstance *obj)
{
IdentifierStruct is(eis);
// Own players are irrelevant here
if (is.isOwnElement())
{
m_newObjectsWithoutParticipantIdSet.remove(obj);
return false;
}
// Already created -> ignore
if (m_observerEnvironmentObjects.contains(is.entityNumber()))
return true;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " will be added.";
m_observerEnvironmentObjects.insert(is.entityNumber(), obj);
m_nonpublishedEnvironmentObjects.insert(is.entityNumber());
m_newObjectsWithoutParticipantIdSet.remove(obj);
const rpr2::IObserverGuezMineFieldObjInstance *minefield = dynamic_cast<const rpr2::IObserverGuezMineFieldObjInstance *>(obj);
if (minefield)
return addMinefield(minefield);
const rpr2::IObserverGuezMineFieldBreachObjInstance *minefieldBreach = dynamic_cast<const rpr2::IObserverGuezMineFieldBreachObjInstance *>(obj);
if (minefieldBreach)
return addMinefieldBreach(minefieldBreach);
return false;
}
bool getCorridorSegment(const rpr2::ArrayOfWorldLocationStructDynamicHLAlengthlessVarArray &pointsDataIn,
std::vector<Core::GeoPosition2D> &segmentsOut,
double &widthOut)
{
// P0 P3
// L *-------------------* L
// i | | i
// n * S0 S1 * n
// e | | e
// 1 *-------------------* 2
// P1 P2
// Calculate width = P0->P1
// Calculate S0, S1
if (pointsDataIn.size() != 4)
return false;
ParticipantPosition p0(pointsDataIn[0]);
ParticipantPosition p1(pointsDataIn[1]);
ParticipantPosition p2(pointsDataIn[2]);
ParticipantPosition p3(pointsDataIn[3]);
LtsCoordTrans::UtmCoordinate utmP0 = LtsCoordTrans::UtmCoordinate::fromGeographic(p0.toPoint());
LtsCoordTrans::UtmCoordinate utmP1 = LtsCoordTrans::UtmCoordinate::fromGeographic(p1.toPoint(), utmP0.zone);
LtsCoordTrans::UtmCoordinate utmP2 = LtsCoordTrans::UtmCoordinate::fromGeographic(p2.toPoint(), utmP0.zone);
LtsCoordTrans::UtmCoordinate utmP3 = LtsCoordTrans::UtmCoordinate::fromGeographic(p3.toPoint(), utmP0.zone);
QLineF line1(utmP0.x, utmP0.y, utmP1.x, utmP1.y);
widthOut = line1.length();
line1.setLength(0.5 * line1.length());
LtsCoordTrans::UtmCoordinate utmS0(utmP0.zone, line1.p2().x(), line1.p2().y(), utmP0.northp);
QLineF line2(utmP2.x, utmP2.y, utmP3.x, utmP3.y);
widthOut = line2.length();
line2.setLength(0.5 * line2.length());
LtsCoordTrans::UtmCoordinate utmS1(utmP0.zone, line2.p2().x(), line2.p2().y(), utmP0.northp);
segmentsOut.resize(2);
segmentsOut[0] = Core::GeoPosition2D(utmS0.geographic().x(), utmS0.geographic().y());
segmentsOut[1] = Core::GeoPosition2D(utmS1.geographic().x(), utmS1.geographic().y());
return true;
}
bool NgsConnector::addMinefield(const rpr2::IObserverGuezMineFieldObjInstance *minefield)
{
int id = minefield->isValidObjectIdentifier() ? IdentifierStruct(minefield->getObjectIdentifier()).entityNumber() : 0;
QTimer::singleShot(STATUS_WAIT_INTERVAL, this, [this, minefield, id]() mutable
{
if (!m_observerEnvironmentObjects.contains(id))
return;
if (!minefield->isValidObjectIdentifier() ||
!minefield->isValidMineType() ||
!minefield->isValidMineCount() ||
!minefield->isValidActivationTime() ||
!minefield->isValidAreaEffectSimulationName() ||
!minefield->isValidForceIdentifier() ||
!minefield->isValidPointsData())
{
ERROR_LOG(getLogger("NgsConnector.Inbound.MineField"))
<< "Wanted to create Minefield but not all necessary data is available yet. :(";
return;
}
if (minefield->isValidPercentComplete() && minefield->getPercentComplete() > 99)
return;
IdentifierStruct is(minefield->getObjectIdentifier());
AreaEffectSimulation::MineAreaModifyRequest ma;
const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct(minefield->getMineType()).toString());
ma.externalId(is.toInt64());
ma.chargeStruct().chargeTypeId(ct.id());
ma.density(0.008 * minefield->getMineCount() + 0.1); // 0 -> 0.1 , 100 -> 0.9
ma.effectStart(getMSecsSinceEpoch(minefield->getActivationTime()));
ma.effectSourceId(ProjectDataManagement::EffectSource::ES_HLA);
ma.name(minefield->getAreaEffectSimulationName().str());
if (minefield->isValidDuration())
ma.effectDuration(60LL * 60 * 1000 * minefield->getDuration()); // Hours to MSecs
else
ma.effectDuration(60LL * 60 * 1000 * 4); // 4 Hour Default
int partyId = m_ngsHandler->ppmHandler()->partyIdFromForceIdentifier(minefield->getForceIdentifier());
ma.partyId(partyId);
double width;
std::vector<Core::GeoPosition2D> segments;
if (!getCorridorSegment(minefield->getPointsData(), segments, width))
{
ERROR_LOG(getLogger("NgsConnector.Inbound.MineField"))
<< "Minefield Id: " << is.toString()
<< ", Name: " << ma.name()
<< ". Could not calculate segment from given points data.";
return;
}
DEBUG_LOG(getLogger("NgsConnector.Inbound.MineField"))
<< "Minefield added/changed: Id: " << is.toString()
<< ", Name: " << ma.name();
ma.segments(segments);
ma.width(width);
ma.isCenterLine(true);
emit newMinefield(ma);
m_nonpublishedEnvironmentObjects.remove(id);
});
return true;
}
bool NgsConnector::addMinefieldBreach(const rpr2::IObserverGuezMineFieldBreachObjInstance *minefieldBreach)
{
QTimer::singleShot(STATUS_WAIT_INTERVAL, this, [this, minefieldBreach]() mutable
{
if (!minefieldBreach->isValidObjectIdentifier() ||
!minefieldBreach->isValidActivationTime() ||
!minefieldBreach->isValidAreaEffectSimulationName() ||
!minefieldBreach->isValidForceIdentifier() ||
!minefieldBreach->isValidPointsData())
return;
IdentifierStruct is(minefieldBreach->getObjectIdentifier());
// If it was deleted, don't do anything!
if (!m_observerEnvironmentObjects.contains(is.entityNumber()))
return;
AreaEffectSimulation::MineCorridorModifyRequest mc;
mc.externalId(is.toInt64());
mc.effectStart(getMSecsSinceEpoch(minefieldBreach->getActivationTime()));
mc.effectSourceId(ProjectDataManagement::EffectSource::ES_HLA);
mc.name(minefieldBreach->getAreaEffectSimulationName().str());
std::vector<Core::GeoPosition2D> vec;
double width;
if (!getCorridorSegment(minefieldBreach->getPointsData(), vec, width))
{
ERROR_LOG(getLogger("NgsConnector.Inbound.MineFieldBreach"))
<< "Minefield Breach Id: " << is.toString()
<< ", Name: " << mc.name()
<< ". Could not calculate segment from given points data.";
return;
}
mc.segments(vec);
mc.width(width);
int partyId = m_ngsHandler->ppmHandler()->partyIdFromForceIdentifier(minefieldBreach->getForceIdentifier());
mc.partyId(partyId);
DEBUG_LOG(getLogger("NgsConnector.Inbound.MineFieldBreach"))
<< "Minefield added/changed: Id: " << is.toString()
<< ", Name: " << mc.name();
emit newMinefieldBreach(mc);
m_nonpublishedEnvironmentObjects.remove(is.entityNumber());
});
return true;
}
bool NgsConnector::onSpatialChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::WorldLocationStruct &wl, qint64 simTimeMSecs)
{
IdentifierStruct is(eis);
ParticipantPosition newParticipantPosition(wl);
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?
}
void NgsConnector::onIsPartOfChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::IsPartOfStruct &ipos, qint64 simTimeMSecs)
{
// We cannot handle Saab players being mounted on GUEZ vehicles and vice versa as on radio the HOST (party A) tells about its mounted players,
// whereas on HLA the player (party B) has to tell where he's mounted on. Party A's Excon cannot publish (IsPartOf) data of the player that
// belongs to party B.
IdentifierStruct is(eis);
if (is.isOwnElement())
return;
int eqNo = is.entityNumber();
int eqNoHost = ipos.HostEntityIdentifier.EntityNumber;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player"))
<< "Player: " << is.toString() << " is now part of " << IdentifierStruct(ipos.HostEntityIdentifier).toString();
emit isPartOfChanged(eqNo, eqNoHost, simTimeMSecs);
}
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::onPointsDataChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::ArrayOfWorldLocationStructDynamicHLAlengthlessVarArray &points, qint64 simTimeMSecs)
{
IdentifierStruct is(eis);
if (is.isOwnElement())
return true;
if (!m_observerEnvironmentObjects.contains(is.entityNumber()))
return false;
if (m_nonpublishedEnvironmentObjects.contains(is.entityNumber()))
return false;
std::vector<Core::GeoPosition2D> pointsData;
for (size_t i = 0; i < points.size(); ++i)
pointsData.resize(pointsData.size() + 1, ParticipantPosition(points[i]).toGeoPosition2D());
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has new Points data (not printed) of size " << pointsData.size();
emit aesPointsDataChanged(is.toInt64(), pointsData, 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::onForceIdentifierChangedEO(const rpr2::EntityIdentifierStruct &eis, rpr2::ForceIdentifierEnum8::ET identifier, qint64 simTimeMSecs)
{
Q_UNUSED(simTimeMSecs);
IdentifierStruct is(eis);
if (is.isOwnElement())
return true;
if (!m_observerEnvironmentObjects.contains(is.entityNumber()))
return false;
if (m_nonpublishedEnvironmentObjects.contains(is.entityNumber()))
return false;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has new Force Identifier: " << identifier << ". Updating AES Item.";
int partyId = m_ngsHandler->ppmHandler()->partyIdFromForceIdentifier(identifier);
emit aesForceIdentifierChanged(is.toInt64(), partyId);
return true;
}
bool NgsConnector::onTacticalStatusChanged(const rpr2::EntityIdentifierStruct &eis,
const rpr2::TacticalStatusStruct &rprStatus,
qint64 simTimeMSecs)
{
IdentifierStruct is(eis);
if (is.isOwnElement())
return true;
ensureDamageModelExists(is);
m_damageModelMap[is.entityNumber()]->setTacticalStatusStruct(rprStatus, simTimeMSecs);
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.Status"))
<< "Player: " << is.toString() << " New Total Status: " << rprStatus.TotalStatus << ", Tactical Status (unused): " << rprStatus.TacticalStatus;
if (rprStatus.TotalStatus == rpr2::TotalStatusEnum32::CombatReady)
{
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.Status")) << "New Status is CombarReady. Therfore we 'init' the player.";
emit participantReceivedInit(is.entityNumber(), simTimeMSecs);
return true;
}
// 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);
if (m_guezHitActive.contains(hitStruct.targetId))
{
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.StatusChange")) << "Tactical Status changed, but GuezHit was detected before. Not doing anything!";
return true;
}
QTimer::singleShot(Settings::guezHitGraceTimeMSecs(), this, [this, hitStruct]() mutable
{
if (m_guezHitActive.contains(hitStruct.targetId))
{
DEBUG_LOG(getLogger("NgsConnector.Inbound.Player.StatusChange")) << "Tactical Status changed, Timeout happened, but GuezHit was detected before. Not doing anything!";
return;
}
WARNING_LOG(getLogger("NgsConnector.Inbound.Player.StatusChange")) << "Tactical Status changed, Timeout happened. NO corresponding GuezHit detected";
emit guezHit(hitStruct);
});
return true;
}
bool NgsConnector::onDeactivatedChanged(const rpr2::EntityIdentifierStruct &eis, bool state, qint64 simTimeMSecs)
{
Q_UNUSED(simTimeMSecs);
IdentifierStruct is(eis);
if (is.isOwnElement())
return true;
if (!m_observerEnvironmentObjects.contains(is.entityNumber()))
return false;
if (m_nonpublishedEnvironmentObjects.contains(is.entityNumber()))
return false;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has changed 'Deactivated' State. New State: " << state;
if (state) // deactivated
{
qint64 activationTime = 0;
const rpr2::IObserverGuezMineFieldObjInstance *minefield = dynamic_cast<const rpr2::IObserverGuezMineFieldObjInstance * >(m_observerEnvironmentObjects[is.entityNumber()]);
if (minefield && minefield->isValidActivationTime())
activationTime = getMSecsSinceEpoch(minefield->getActivationTime());
const rpr2::IObserverGuezMineFieldBreachObjInstance *minefieldBreach = dynamic_cast<const rpr2::IObserverGuezMineFieldBreachObjInstance * >(m_observerEnvironmentObjects[is.entityNumber()]);
if (minefieldBreach && minefieldBreach->isValidActivationTime())
activationTime = getMSecsSinceEpoch(minefieldBreach->getActivationTime());
// again!
if (activationTime > 0)
emit aesRemoveItem(is.toInt64());
}
return true;
}
bool NgsConnector::onPercentCompleteChanged(const rpr2::EntityIdentifierStruct &eis, ngs::UINT32 state, qint64 simTimeMSecs)
{
Q_UNUSED(simTimeMSecs);
IdentifierStruct is(eis);
if (is.isOwnElement())
return true;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has changed 'Percent Complete'. New Completeness: " << state;
// Find out, if it was active before
// Todo: This might not work as the instance's deactivated flag most likely already is set...
if (!m_observerEnvironmentObjects.contains(is.entityNumber()))
return false;
if (state > 99)
emit aesRemoveItem(is.toInt64());
return true;
}
// Minefield
// Minefield Breach
void NgsConnector::onAreaEffectSimulationNameChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::OMT13string &value, qint64 simTimeMSecs)
{
Q_UNUSED(simTimeMSecs);
IdentifierStruct is(eis);
if (is.isOwnElement())
return;
if (m_nonpublishedEnvironmentObjects.contains(is.entityNumber()))
return;
if (!m_observerEnvironmentObjects.contains(eis.EntityNumber))
return;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has changed Name. New Name: " << QString::fromStdString(value);
emit aesNameChanged(is.toInt64(), value);
}
// Minefield
// Minefield Breach
void NgsConnector::onActivationTimeChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::ClockTimeStruct &value, qint64 simTimeMSecs)
{
Q_UNUSED(simTimeMSecs);
IdentifierStruct is(eis);
if (is.isOwnElement())
return;
if (m_nonpublishedEnvironmentObjects.contains(is.entityNumber()))
return;
if (!m_observerEnvironmentObjects.contains(eis.EntityNumber))
return;
qint64 msecs = getMSecsSinceEpoch(value);
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has changed Activation Time: " << msecs;
emit aesActivationTimeChanged(is.toInt64(), msecs);
}
// Minefield
void NgsConnector::onDurationChanged(const rpr2::EntityIdentifierStruct &eis, ngs::UINT32 value, qint64 simTimeMSecs)
{
Q_UNUSED(simTimeMSecs);
IdentifierStruct is(eis);
if (is.isOwnElement())
return;
if (m_nonpublishedEnvironmentObjects.contains(is.entityNumber()))
return;
if (!m_observerEnvironmentObjects.contains(eis.EntityNumber))
return;
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has changed Duration: " << value;
emit aesDurationChanged(is.toInt64(), value);
}
// Minefield
void NgsConnector::onMineTypeChanged(const rpr2::EntityIdentifierStruct &eis, const rpr2::EntityTypeStruct &ets, qint64 simTimeMSecs)
{
IdentifierStruct is(eis);
Q_UNUSED(simTimeMSecs);
if (is.isOwnElement())
return;
if (!m_observerEnvironmentObjects.contains(is.entityNumber()))
return;
if (m_observerEnvironmentObjects.contains(eis.EntityNumber))
return;
const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct(ets).toString());
DEBUG_LOG(getLogger("NgsConnector.Inbound.Object"))
<< "Object: " << is.toString() << " has changed Munition: " << ct.disEntity() << " / internal id: " << ct.id();
emit aesChargeChanged(is.toInt64(), ct.id());
}
// HLA -> DDS
void NgsConnector::onNewMunitionDetonation(const rpr2::IObserverMunitionDetonationIntInstance &interaction)
{
// Own Item?
if (std::stol(interaction.getEventIdentifier().IssuingObjectIdentifier.ID) == IdentifierStruct(0).toInt64())
return;
//
// create effect at EXCON ...
//
//
// ... based on ExplosionClusterSingleEffectOnly
//
{
// try to create an ExplosionCluster 'light' (point geometry, no trajectory)
ExplosionClusterSingleEffectOnlyRequest request;
// id
request.id(0);
// externalId
IdentifierStruct is(interaction.getEventIdentifier().EventCount);
is.setSiteId(255); // Dummy - Must not be of either site.
request.externalId(is.toInt64()); // dummy val, inserted to prevent aes_handler from dropping this telegram
// effectStart, duration
request.effectStart(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch() + ACTIVATION_LEAD_TIME);
// geometry
request.impactPosition( ParticipantPosition(interaction.getDetonationLocation()).toGeoPosition2D() );
// charge
const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct(interaction.getMunitionType()).toString());
request.chargeTypeId(ct.id());
// names
const QString name = tr("SAAB Fire: %1").arg(interaction.getEventIdentifier().EventCount);
request.name(name.toStdString()); // ZpNummer
request.element(request.name()); // element
DEBUG_LOG(getLogger("NgsConnector.Inbound.MunitionDetonation"))
<< "Munition Detonation: "
<< "Location: " << ParticipantPosition(interaction.getDetonationLocation()).toString()
<< ", Charge Type: " << ct.disEntity();
emit munitionDetonationExplosionClusterSingleEffectOnly(request);
return;
}
//
// ... based on ExplosionSingleModifyRequest
//
AreaEffectSimulation::ExplosionSingleModifyRequest req;
// req.altitude();
const auto ct = m_ngsHandler->ppmHandler()->chargeTypeFromDisEntity(TypeStruct(interaction.getMunitionType()).toString());
req.chargeTypeId(ct.id());
req.id(0);
req.effectStart(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch() + ACTIVATION_LEAD_TIME);
req.effectSourceId(ProjectDataManagement::EffectSource::ES_HLA);
IdentifierStruct is(interaction.getEventIdentifier().EventCount);
is.setSiteId(255); // Dummy - Must not be of either site.
req.externalId(is.toInt64()); // dummy val, inserted to prevent aes_handler from dropping this telegram
req.fireRequestTime(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
req.impactPosition(ParticipantPosition(interaction.getDetonationLocation()).toGeoPosition2D());
// IdentifierStruct is = IdentifierStruct::fromInt64(QString::fromStdString(interaction.getEventIdentifier().IssuingObjectIdentifier.ID).toLongLong());
req.name(tr("SAAB Fire: %1").arg(interaction.getEventIdentifier().EventCount).toStdString());
// Todo: If SAAB sends the Party in interaction.getFiringObjectIdentifier().ID like "F12" -> "ForceIdentifierEnum -> Value 12"
// req.partyId(interaction.getFiringObjectIdentifier())
// req.purpose()
// req.rotation()
DEBUG_LOG(getLogger("NgsConnector.Inbound.MunitionDetonation"))
<< "Munition Detonation: "
<< "Location: " << ParticipantPosition(interaction.getDetonationLocation()).toString()
<< ", Charge Type: " << ct.disEntity();
emit munitionDetonation(req);
}
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();
}
}
// Saab Players
for (auto it(m_observerPlayerInstances.cbegin()); it != m_observerPlayerInstances.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)
{
// Self issued?
if (std::stol(interaction.getEventIdentifier().IssuingObjectIdentifier.ID) == IdentifierStruct(0).toInt64())
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());
charge.chargeTypeId(ct.id());
// charge.gearId();
// charge.gearTypeId();
// charge.remain();
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.disEntity()
<< ", Shooter: " << event.equipmentNumber()
<< ", Rounds: " << interaction.getQuantityFired() << ")";
emit weaponFire(event);
}
void NgsConnector::onNewGuezHit(const rpr2::IObserverGuezHitIntInstance &interaction)
{
IdentifierStruct is(interaction.getTargetIdentifier());
if (is.isOwnElement())
return;
if (!m_observerPlayerInstances.contains(is.entityNumber()))
{
ERROR_LOG(getLogger("NgsConnector.Inbound.GuezHit"))
<< "Player: " << is.toString()
<< ", but there's no observer Object for it.";
return;
}
const int eqNo = interaction.getTargetIdentifier().EntityNumber;
auto ptr = m_damageModelMap.value(eqNo);
if (!ptr)
{
WARNING_LOG(getLogger("NgsConnector.Inbound.GuezHit"))
<< "Player: " << eqNo
<< ", Damage Model no longer exists. Not processing GuezHit!";
return;
}
ptr->setTacticalStatus(interaction.getHitResult(), simTimeToMSec(interaction.getReceiveTimeStamp()));
GuezHitStruct hitStruct;
hitStruct.originatorChargeTypeEtsString = TypeStruct(interaction.getMunition()).toString();
hitStruct.originatorId = interaction.getOriginatorIdentifier().EntityNumber;
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::Unknown;
switch (interaction.getOriginatorType())
{
case rpr2::OriginatorTypeEnum16::Unknown:
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::Unknown;
break;
case rpr2::OriginatorTypeEnum16::NoMedicalTreatment: // If no medical treatment has been conducted, and there's no originator, he's bleeding for himself...
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::Participant;
if (hitStruct.originatorId == 0) // for sanity, there can't be a participant with EqId = 0
hitStruct.originatorId = hitStruct.targetId;
break;
case rpr2::OriginatorTypeEnum16::Participant:
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::Participant;
if (hitStruct.originatorId == 0) // for sanity, there can't be a participant with EqId = 0
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::ExerciseControl;
break;
case rpr2::OriginatorTypeEnum16::AreaEffectSimulation:
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::AreaEffectSimulation;
hitStruct.originatorId = IdentifierStruct(interaction.getOriginatorIdentifier()).toInt64();
break;
case rpr2::OriginatorTypeEnum16::ObserverController:
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::ObserverController;
break;
case rpr2::OriginatorTypeEnum16::ExerciseControl:
hitStruct.originatorType = ProjectDataManagement::StateChangeOriginator_def::ExerciseControl;
break;
}
hitStruct.position = ParticipantPosition(interaction.getTargetLocation()).toGeoPosition();
hitStruct.sampleTime = simTimeToMSec(interaction.getReceiveTimeStamp());
hitStruct.targetId = interaction.getTargetIdentifier().EntityNumber;
// hitStruct.vulnerabilityState = ProjectDataManagement::VulnerabilityState_def::VulnerabilityBitsChanged; // --> Evaluate State from Bits (done in PPM Service).
// hitStruct.vulnerabilityKind = 0;
hitStruct.vulnerabilityState = getTacticalStatusEnum(interaction.getHitResult());
// HAck for resetting a player, whilst SAAB does not send the correct HitResult...
if ((hitStruct.originatorType == ProjectDataManagement::StateChangeOriginator_def::ExerciseControl ||
hitStruct.originatorType == ProjectDataManagement::StateChangeOriginator_def::ObserverController
)
&& hitStruct.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::HitWithNoEffect)
{
auto ptr = m_damageModelMap.value(eqNo);
if (ptr)
{
hitStruct.vulnerabilityState = ptr->vulnerabilityState(hitStruct.sampleTime);
if (hitStruct.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CombatReady)
ptr->setTacticalStatus(rpr2::HitResultEnum16::Reset, simTimeToMSec(interaction.getReceiveTimeStamp()));
}
}
DEBUG_LOG(getLogger("NgsConnector.Inbound.GuezHit"))
<< "Player: " << is.toString()
<< ", Originator Type: " << hitStruct.originatorType
<< ", Originator Id: " << hitStruct.originatorId
<< ", Charge Type: " << hitStruct.originatorChargeTypeEtsString
<< ", HitResult: " << hitStruct.vulnerabilityState;
m_guezHitActive.insert(hitStruct.targetId);
QTimer::singleShot(Settings::guezHitGraceTimeMSecs(), this, [this, hitStruct, eqNo]() mutable
{
auto ptr = m_damageModelMap.value(eqNo);
if (!ptr)
{
WARNING_LOG(getLogger("NgsConnector.Inbound.GuezHit"))
<< "Player: " << eqNo
<< ", Damage Model no longer exists. Not processing GuezHit!";
return;
}
const auto vulnerabilityBits = ptr->totalState(hitStruct.sampleTime);
hitStruct.vulnerabilityBits = (vulnerabilityBits & ~rpr2::TotalStatusEnum32::Manipulation);
DEBUG_LOG(getLogger("NgsConnector.Inbound.GuezHit")) << "vBits: " << vulnerabilityBits;
DEBUG_LOG(getLogger("NgsConnector.Inbound.GuezHit")) << "vState: " << hitStruct.vulnerabilityState;
m_guezHitActive.remove(hitStruct.targetId);
emit guezHit(hitStruct);
});
}
void NgsConnector::onNewGuezInit(const rpr2::IObserverGuezInitIntInstance &interaction)
{
// ExConEngagement FROM HLA TO US! Our Player shall be affected!
IdentifierStruct is(interaction.getEntityIdentifier());
if (!is.isOwnElement())
return;
if (!m_playerMap.contains(is.entityNumber()))
{
ERROR_LOG(getLogger("NgsConnector.Inbound.GuezInit"))
<< "Player: " << is.toString()
<< " is not our player...";
return;
}
rpr2::ResetPlayerEnum8::ET rp = interaction.getResetPlayer();
rpr2::ActivateHelmetObservationEnum8::ET aho = interaction.getActivateHelmetObservation();
bool resetVulnerabilityState = true;
if (rp != rpr2::ResetPlayerEnum8::CombatReadyWithoutReammunition &&
rp != rpr2::ResetPlayerEnum8::CombatReadyWithReammunition)
resetVulnerabilityState = false;
if (aho != rpr2::ActivateHelmetObservationEnum8::NoChange)
{
WARNING_LOG(getLogger("NgsConnector.Inbound.GuezInit"))
<< "Player: " << is.toString()
<< "Incoming Activation Helmet/Vest on GuezInit not supported yet";
}
DEBUG_LOG(getLogger("NgsConnector.Inbound.GuezInit"))
<< "Player: " << is.toString()
<< ", Reset Type: " << rp;
emit guezInit(is.entityNumber(), false, rp == rpr2::ResetPlayerEnum8::CombatReadyWithReammunition, resetVulnerabilityState);
}
void NgsConnector::onNewGuezBiteReport(const rpr2::IObserverGuezBiteReportIntInstance &interaction)
{
IdentifierStruct is(interaction.getEntityIdentifier());
if (is.isOwnElement())
return;
TrainingAreaBase::EquipmentBite eb;
QStringList result;
bool leave = false;
if (!interaction.isValidBiteResult())
{
result << QStringLiteral("BiteResult not valid");
leave = true;
}
if (!interaction.isValidConfigurationComparisonResult())
{
result << QStringLiteral("ConfigurationComparison not valid");
leave = true;
}
if (!interaction.isValidDiscoveredDevices())
{
result << QStringLiteral("DiscoveredDevices not valid");
leave = true;
}
if (!interaction.isValidEntityIdentifier())
{
result << QStringLiteral("EntityIdentifier not valid");
leave = true;
}
if (!interaction.isValidRegistrationType())
{
result << QStringLiteral("RegistrationType not valid");
leave = true;
}
if (!interaction.isValidReportTime())
{
result << QStringLiteral("ReportTime not valid");
leave = true;
}
if (leave)
{
ERROR_LOG(getLogger("NgsConnector.Inbound.GuezBiteReport"))
<< "Player: " << is.toString()
<< ", Cannot process Interaction. Reason(s): " << result.join(QLatin1String(", "));
return;
}
rpr2::BiteResultStruct brs = interaction.getBiteResult();
eb.configState(brs.DeviceOkay);
eb.batteryState(brs.BatteryWarning);
std::vector<TrainingAreaBase::EquipmentBiteError> be;
be.resize(brs.ErrorCodes.size());
for (unsigned int i = 0; i < brs.ErrorCodes.size(); ++i)
be[i].canDeviceNumber(brs.ErrorCodes[i].DeviceId);
eb.biteErrors(be);
eb.biteState(interaction.getDiscoveredDevices());
eb.configError(interaction.getConfigurationComparisonResult());
eb.equipmentNumber(is.entityNumber());
eb.registerType(interaction.getRegistrationType());
eb.sampleTime(getMSecsSinceEpoch(interaction.getReportTime()));
DEBUG_LOG(getLogger("NgsConnector.Inbound.GuezBiteReport"))
<< "Player: " << is.toString()
<< ", Device Okay: " << brs.DeviceOkay
<< ", Battery Warning: " << brs.BatteryWarning
<< ", Bite Error Count: " << be.size()
<< ", Registration Type: " << eb.registerType()
<< ", Config Error: " << eb.configError();
emit biteReport(eb);
}
void NgsConnector::onNewGuezRequestBiteReport(const rpr2::IObserverGuezRequestBiteReportIntInstance &interaction)
{
IdentifierStruct is(interaction.getEntityIdentifier());
if (!is.isOwnElement())
return;
DEBUG_LOG(getLogger("NgsConnector.Inbound.GuezBiteRequest"))
<< "Player: " << is.toString()
<< ", Bite Report Type: " << interaction.getBiteReportType();
TrainingAreaBase::DemandBiteRequest dbr;
dbr.equipmentNumbers().push_back(is.entityNumber());
dbr.requestId(1);
dbr.withSelfTest(interaction.getBiteReportType() == rpr2::BiteReportTypeEnum8::StatusAfterSelftest);
emit biteRequest(dbr);
}
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;
}
rpr2::TacticalStatusStruct NgsConnector::getTacticalStatusFromState(const ParticipantManagement::StateInformation &state)
{
rpr2::TacticalStatusStruct ts;
ts.TacticalStatus = static_cast<rpr2::TacticalStatusEnum8::ET>(state.vulnerabilityState().underlying());
ts.TotalStatus = static_cast<rpr2::TotalStatusEnum32::ET>(state.vulnerabilityBits());
ts.VulnerabilityKind = state.vulnerabilityKindId();
return ts;
}
bool NgsConnector::setVulnerabilityState(int eqNo, const ParticipantManagement::StateInformation &state)
{
rpr2::TacticalStatusStruct ts = getTacticalStatusFromState(state);
rpr2::IPlayerPhysicalEntityObjInstance *playerInstance = dynamic_cast<rpr2::IPlayerPhysicalEntityObjInstance *>(m_playerMap.value(eqNo, nullptr));
if (!playerInstance)
return false;
DamageModel dm(ts);
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: " << ts.TotalStatus
<< ", New Tactical Status (shall be unused): " << ts.TacticalStatus;
rpr2::IPlayerGuezHumanObjInstance *humanInstance = dynamic_cast<rpr2::IPlayerGuezHumanObjInstance *>(playerInstance);
if (humanInstance)
{
humanInstance->setTacticalStatus(ts);
playerInstance->publish(getSimTime(state.stateTime()));
return true;
}
rpr2::IPlayerGuezGroundVehicleObjInstance *groundVehicleInstance = dynamic_cast<rpr2::IPlayerGuezGroundVehicleObjInstance *>(playerInstance);
if (groundVehicleInstance)
{
groundVehicleInstance->setTacticalStatus(ts);
playerInstance->publish(getSimTime(state.stateTime()));
return true;
}
ERROR_LOG(getLogger("NgsConnector.Outbound.Player.Status"))
<< "Player: " << eqNo
<< " is not Human or GroundVehicle. Cannot set status.";
return false;
}
// Hit from Participant Management
bool NgsConnector::setParticipantHit(int eqNo, const ParticipantManagement::StateInformation &state,
const ParticipantPosition &partPos, int chargeType,
ProjectDataManagement::StateChangeOriginator originatorType, int originatorEqNo)
{
// 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());
icb.setHitResult(getHitResultEnum(state.vulnerabilityState()));
IdentifierStruct is(0); // own excon
rpr2::OriginatorTypeEnum16::ET originatorTypeRpr = rpr2::OriginatorTypeEnum16::Unknown;
switch (originatorType.underlying())
{
case ProjectDataManagement::StateChangeOriginator_def::AreaEffectSimulation:
{
if (m_environmentObjects.contains(originatorEqNo)) // It's our AesItem, originatorEqNo is AesItemId is Entity Number
is = IdentifierStruct(10 * originatorEqNo);
else
{
// Obtain ExternalId
int64_t externalId = m_ngsHandler->aesHandler()->externalId(originatorEqNo);
// At this map, Entities and Objects are
if (m_observerPlayerInstances.contains(externalId))
{
const rpr2::IObserverBaseEntityObjInstance *instance = dynamic_cast<const rpr2::IObserverBaseEntityObjInstance * >(m_observerPlayerInstances[originatorEqNo]);
if (instance->isValidEntityIdentifier())
is = IdentifierStruct(instance->getEntityIdentifier());
}
}
originatorTypeRpr = rpr2::OriginatorTypeEnum16::AreaEffectSimulation;
break;
}
case ProjectDataManagement::StateChangeOriginator_def::ExerciseControl:
originatorTypeRpr = rpr2::OriginatorTypeEnum16::ExerciseControl;
break;
case ProjectDataManagement::StateChangeOriginator_def::ObserverController:
originatorTypeRpr = rpr2::OriginatorTypeEnum16::ObserverController;
break;
case ProjectDataManagement::StateChangeOriginator_def::Participant:
if (originatorType == ProjectDataManagement::StateChangeOriginator_def::Participant)
{
if (m_playerMap.contains(originatorEqNo))
is = IdentifierStruct(originatorEqNo);
else if (m_observerPlayerInstances.contains(originatorEqNo))
{
// Works as long as externalID == equipmentNo
const rpr2::IObserverBaseEntityObjInstance *instance = dynamic_cast<const rpr2::IObserverBaseEntityObjInstance * >(m_observerPlayerInstances[originatorEqNo]);
if (instance->isValidEntityIdentifier())
is = IdentifierStruct(instance->getEntityIdentifier());
}
originatorTypeRpr = rpr2::OriginatorTypeEnum16::Participant;
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:
case ProjectDataManagement::StateChangeOriginator_def::MedicalTimeout:
case ProjectDataManagement::StateChangeOriginator_def::Sanitation:
// Todo: Check, if IED and DUSMine actually can be handled correctly:
case ProjectDataManagement::StateChangeOriginator_def::ImprovisedExplosiveDevice:
case ProjectDataManagement::StateChangeOriginator_def::DusMine:
originatorTypeRpr = rpr2::OriginatorTypeEnum16::Unknown;
break;
}
icb.setOriginatorIdentifier(is.toEIS());
icb.setOriginatorType(originatorTypeRpr);
DEBUG_LOG(getLogger("NgsConnector.Outbound.GuezHit"))
<< "Player: " << eqNo
<< ", Originator Type: " << originatorType
<< ", Originator EIS: " << is.toString()
<< ", Charge Type: " << TypeStruct(getEtsCharge(chargeType)).toString()
<< ", 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;
}
bool NgsConnector::setWeaponFire(int eqNo, const ParticipantManagement::ParticipantGearEvent &data)
{
rpr2::IPlayerWeaponFireIntClass &ic = m_somPlayer->getPlayerWeaponFireIntClass();
rpr2::IPlayerWeaponFireIntInstance &icb = ic.newWeaponFireIntInstance();
// FiringInstance ermitteln
rpr2::RTIObjectIdStruct rSource;
rSource.ID = std::to_string(IdentifierStruct(0).toInt64()); // Own Excon
if (eqNo > 0)
{
auto *playerInstanceSource = m_playerMap.value(eqNo);
if (!playerInstanceSource || !playerInstanceSource->isValidObjectName() || !playerInstanceSource->isValidSpatial())
return false;
rSource.ID = playerInstanceSource->getObjectName();
icb.setFiringLocation(playerInstanceSource->getSpatial().DeadReckoningAlgorithm__A__Alternatives.SpatialStatic().WorldLocation);
}
icb.setEventIdentifier(getEventIdentifierStruct());
// icb.setFireControlSolutionRange();
// icb.setFireMissionIndex();
icb.setFiringObjectIdentifier(rSource);
icb.setFuseType(rpr2::FuseTypeEnum16::Other);
icb.setInitialVelocityVector(rpr2::VelocityVectorStruct());
// icb.setMunitionObjectIdentifier();
icb.setMunitionType(getEtsCharge(data.charge().chargeTypeId()));
icb.setQuantityFired(data.chargeAmount());
// icb.setRateOfFire();
// icb.setTargetObjectIdentifier();
icb.setWarheadType(rpr2::WarheadTypeEnum16::Kinetic);
icb.publish(getSimTime(data.sampleTime()));
DEBUG_LOG(getLogger("NgsConnector.Outbound.WeaponFire"))
<< "Weapon Fire: "
<< "Charge Type: " << data.charge().chargeTypeId()
<< ", Shooter: " << eqNo
<< ", Rounds: " << data.chargeAmount();
return true;
}
bool NgsConnector::setEquipmentBite(int eqNo, const TrainingAreaBase::EquipmentBite &data)
{
IdentifierStruct is = getIdentifierStructbyEquipmentNo(eqNo);
if (is.isNull() || !is.isOwnElement())
return false;
rpr2::IPlayerGuezBiteReportIntClass &icx = m_somPlayer->getPlayerGuezBiteReportIntClass();
rpr2::IPlayerGuezBiteReportIntInstance *icb = &icx.newGuezBiteReportIntInstance();
rpr2::BiteResultStruct brs;
brs.BatteryWarning = static_cast<rpr2::BatteryWarningEnum32::ET>(data.batteryState());
brs.DeviceOkay = static_cast<rpr2::CanDeviceEnum32::ET>(data.configState());
rpr2::ErrorStructVariableArray errorCodes;
errorCodes.resize(data.biteErrors().size());
int i = 0;
for (const auto &error : data.biteErrors())
{
errorCodes[i].DeviceId = error.canDeviceNumber();
errorCodes[i].SubDeviceId = error.subdeviceNumber();
errorCodes[i].UniqueSubdeviceId = error.uniqueId();
errorCodes[i].ErrorId = error.errorNumber();
++i;
}
brs.ErrorCodes = errorCodes;
icb->setBiteResult(brs);
icb->setConfigurationComparisonResult(data.configError());
icb->setDiscoveredDevices(static_cast<rpr2::CanDeviceEnum32::ET>(data.biteState()));
icb->setEntityIdentifier(is.toEIS());
icb->setRegistrationType(static_cast<rpr2::RegistrationTypeEnum8::ET>(data.registerType()));
icb->setReportTime(getClockTimeStruct(data.sampleTime()));
icb->publish(getSimTime(data.sampleTime()));
DEBUG_LOG(getLogger("NgsConnector.Outbound.GuezBiteReport"))
<< "Player: " << is.toString()
<< ", Device Okay: " << brs.DeviceOkay
<< ", Battery Warning: " << brs.BatteryWarning
<< ", Bite Error Count: " << errorCodes.size()
<< ", Registration Type: " << data.registerType()
<< ", Config Error: " << data.configError();
return true;
}
} // namespace HighLevelArchitecture