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

1777 lines
65 KiB
C++
Executable File

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