1893 lines
69 KiB
C++
Executable File
1893 lines
69 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Interface
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
#include "participant_to_map.h"
|
|
#include "pdm_handler.h"
|
|
|
|
#include <Map/map.h>
|
|
#include <Map/object_factory.h>
|
|
#include <Map/layer_factory.h>
|
|
#include <Map/party_id_to_color_functor.h>
|
|
#include <PluginInterfaces/gui_plugin_interface.h>
|
|
#include <LtsUtilsCore/outdated_position.h>
|
|
#include <LtsSession/lts_session.h>
|
|
#include <LtsSession/lts_session_data.h>
|
|
|
|
#include <set> // std::set
|
|
|
|
#include <Symbology/symbol_data_to_render_role.h>
|
|
#include <ProjectDataManagement/pdm_to_string.h>
|
|
#include <ParticipantManagement/ppm_enums.hpp>
|
|
#include <QJsonDocument>
|
|
|
|
//Qt
|
|
#include <QElapsedTimer>
|
|
#include <QTimer>
|
|
|
|
|
|
namespace SituationMap
|
|
{
|
|
|
|
static QString participantToString(const ParticipantManagement::Participant &data)
|
|
{
|
|
QString str;
|
|
|
|
str.append(QStringLiteral("participantId=%1").arg(data.id()));
|
|
str.append(QStringLiteral(", symbol=%1").arg(QString::fromStdString(data.symbol())));
|
|
str.append(QStringLiteral(", typeId=%1").arg(data.typeId()));
|
|
str.append(QStringLiteral(", designator=%1").arg(QString::fromStdString(data.designator())));
|
|
str.append(QStringLiteral(", tacticalIdentifier=%1").arg(QString::fromStdString(data.tacticalIdentifier())));
|
|
return str;
|
|
}
|
|
|
|
static bool hasGeoPositionNaN(const Core::GeoPosition &geoPos)
|
|
{
|
|
return std::isnan(geoPos.lat()) ||
|
|
std::isnan(geoPos.lon()) ||
|
|
std::isnan(geoPos.z());
|
|
}
|
|
|
|
|
|
Participant2SituationMap::Participant2SituationMap(PdmHandler *pdmHandler,
|
|
ObjectFactory &objFac,
|
|
const QString &layername,
|
|
const char *loggerName)
|
|
: BaseClass(objFac, layername, loggerName)
|
|
, FilterClass(loggerName, this)
|
|
, m_pdmHandler(pdmHandler)
|
|
, m_fireEventFlashTimer(new QTimer(this))
|
|
{
|
|
m_fireEventFlashTimer->setInterval(1000);
|
|
connect(m_fireEventFlashTimer, &QTimer::timeout, this, &Participant2SituationMap::onFireEventFlashTimer);
|
|
}
|
|
|
|
Participant2SituationMap::~Participant2SituationMap()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
// Update GUI (reset every ParticipantInfoDialog)
|
|
if (m_movementTrackManager)
|
|
m_movementTrackManager->emit_activeSimIdsChanged(QVector<int>()); // empty
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
int32_t Participant2SituationMap::getSimId(const Sim2SituationMap::DDS_T &ddsData) const
|
|
{
|
|
return ddsData.id();
|
|
}
|
|
|
|
bool Participant2SituationMap::init(LayerFactory *layerFactory,
|
|
PluginProvider *pluginProvider)
|
|
{
|
|
if (!layerFactory ||
|
|
!pluginProvider ||
|
|
!pluginProvider->ppmDtc() ||
|
|
!pluginProvider->aesDtc())
|
|
return false;
|
|
|
|
m_layerFactory = layerFactory;
|
|
m_pluginProvider = pluginProvider;
|
|
|
|
m_ppmDtc = m_pluginProvider->ppmDtc();
|
|
m_aesDtc = m_pluginProvider->aesDtc();
|
|
m_ppmParticipantInfoIfc = m_pluginProvider->ppmParticipantInfoIfc();
|
|
m_ocParticipantInfoIfc = m_pluginProvider->ocParticipantInfoIfc();
|
|
|
|
// FilterManagement
|
|
FilterClass::init(m_pluginProvider);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Participant2SituationMap::run()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
if (!m_ppmDtc)
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " no dtc";
|
|
return false;
|
|
}
|
|
|
|
this->stop();
|
|
|
|
if (!m_pluginProvider->isSessionValid())
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " no m_session anymore: return";
|
|
return false;
|
|
}
|
|
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::typeChanged,
|
|
this, &Participant2SituationMap::onTypesChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
|
|
this, &Participant2SituationMap::onParticipantChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged,
|
|
this, &Participant2SituationMap::onPositionChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::unitPositionChanged,
|
|
this, &Participant2SituationMap::onUnitPositionChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged,
|
|
this, &Participant2SituationMap::onDynamicParticipantDataChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::biteChanged,
|
|
this, &Participant2SituationMap::onBiteChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
// get:
|
|
auto cbTypes = [this](const ParticipantManagement::Type & type)
|
|
{
|
|
onTypesChanged(type, DataModification::eAdded);
|
|
};
|
|
m_ppmDtc->types(cbTypes);
|
|
updateHierarchyLevels();
|
|
|
|
// nur lesen, kein Update (wird in den anderen Funktionen indirekt verwendet)
|
|
m_ppmDtc->bites([this](const ParticipantManagement::Bite & data)
|
|
{
|
|
if (data.biteErrors().size() > 0)
|
|
m_hasBiteError.insert(data.equipmentNumber());
|
|
});
|
|
// nur lesen, kein Update (wird in den anderen Funktionen indirekt verwendet)
|
|
m_ppmDtc->dynamicParticipantDatas([this](const ParticipantManagement::DynamicParticipantData & data)
|
|
{
|
|
m_participantDynamicData.insert(data.participantId(), data);
|
|
});
|
|
// Anlegen der Objekte
|
|
m_ppmDtc->participants([this](const ParticipantManagement::Participant & data)
|
|
{
|
|
onParticipantChanged(data, DataModification::eAdded);
|
|
});
|
|
// Aktualiserung der Position
|
|
m_ppmDtc->participantPositions([this](const ParticipantManagement::ParticipantPosition & data)
|
|
{
|
|
onPositionChanged(data, DataModification::eAdded);
|
|
});
|
|
m_ppmDtc->unitPositions([this](const ParticipantManagement::UnitPosition & data)
|
|
{
|
|
onUnitPositionChanged(data, DataModification::eAdded);
|
|
});
|
|
|
|
//
|
|
// Aes
|
|
//
|
|
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
|
|
this, &Participant2SituationMap::onAesItemViewChanged,
|
|
Qt::UniqueConnection);
|
|
m_aesDtc->aesItemViews([this](const AreaEffectSimulation::AesItemView & data)
|
|
{
|
|
onAesItemViewChanged(data, DataModification::eAdded);
|
|
});
|
|
|
|
// to handle race condition between DynamicParticipantData and DusMineState:
|
|
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::dusMineStateChanged,
|
|
this, &Participant2SituationMap::onDusMineStateChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
|
|
//
|
|
// FireEventFlashing:
|
|
//
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantGearEventChanged,
|
|
this, &Participant2SituationMap::onParticipantGearEventChanged,
|
|
Qt::UniqueConnection);
|
|
|
|
m_ppmDtc->participantGearEvents([this](const ParticipantManagement::ParticipantGearEvent & data)
|
|
{
|
|
onParticipantGearEventChanged(data, DataModification::eAdded);
|
|
});
|
|
|
|
// Timer:
|
|
if (m_fireEventFlashEnable)
|
|
m_fireEventFlashTimer->start();
|
|
|
|
connect(m_pluginProvider, &PluginProvider::partitionTimeChanged,
|
|
this, &Participant2SituationMap::onPartitionTimeChanged);
|
|
|
|
// At Snapshot partitionTimeChanged is signaled just once.
|
|
// 'Simulate' that with m_notifiyTimer:
|
|
if ((m_pluginProvider->appId() == LtsAppDescriptor::App::Snapshot) ||
|
|
((m_pluginProvider->appId() == LtsAppDescriptor::App::Situation /*!!! Situation !!!*/ ) && m_pluginProvider->isReplayPartition()) || // snapshotTopic within script
|
|
((m_pluginProvider->appId() == LtsAppDescriptor::App::PlanEditor) && (m_pluginProvider->partitionId() == 0)) // PlanEditor @ partitionId==0 -> PE on Snapshot
|
|
)
|
|
{
|
|
if (!m_notifiyTimer)
|
|
{
|
|
m_notifiyTimer = new QTimer(this);
|
|
m_notifiyTimer->setInterval(1000);
|
|
connect(m_notifiyTimer, &QTimer::timeout,
|
|
this, &Participant2SituationMap::notifyMapAboutModifedObjects);
|
|
}
|
|
m_notifiyTimer->start();
|
|
}
|
|
|
|
// MovementTrack :
|
|
if (m_movementTrackManager)
|
|
{
|
|
connect(m_movementTrackManager.data(), &MovementTrackManager::trackActivityChanged,
|
|
this, &Participant2SituationMap::onMovementTrackActivityChanged);
|
|
connect(m_movementTrackManager.data(), &MovementTrackManager::trackRemoved,
|
|
this, &Participant2SituationMap::onMovementTrackRemoved);
|
|
|
|
// transfer tracks to a) participants and b) update GUI:
|
|
m_movementTrackManager->setCurrentTimeMsec(m_pluginProvider->partitionTime());
|
|
{
|
|
const QVector<int> &simIds = m_movementTrackManager->activeTrackIds();
|
|
for (int simId : simIds)
|
|
onMovementTrackActivityChanged(simId, true);
|
|
}
|
|
{
|
|
const QVector<int> &simIds = m_movementTrackManager->deactiveTrackIds();
|
|
for (int simId : qAsConst(simIds))
|
|
onMovementTrackActivityChanged(simId, false);
|
|
}
|
|
}
|
|
|
|
// to apply values from filter to MomentTrackManager (like maxViewAge):
|
|
onSymbolAppearanceSettingsChanged(FilterManagement::SymbolAppearanceSettings::Part::All);
|
|
// : MovementTrack
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
void Participant2SituationMap::stop()
|
|
{
|
|
// FlashTimer:
|
|
if (m_fireEventFlashTimer)
|
|
m_fireEventFlashTimer->stop();
|
|
|
|
if (m_notifiyTimer)
|
|
m_notifiyTimer->stop();
|
|
|
|
if (m_aesDtc)
|
|
m_aesDtc->disconnect(this);
|
|
if (m_ppmDtc)
|
|
m_ppmDtc->disconnect(this);
|
|
|
|
if (m_pluginProvider)
|
|
disconnect(m_pluginProvider, &PluginProvider::partitionTimeChanged,
|
|
this, &Participant2SituationMap::onPartitionTimeChanged);
|
|
|
|
if (m_movementTrackManager)
|
|
m_movementTrackManager->disconnect(this);
|
|
|
|
m_participants.clear();
|
|
m_participantPositions.clear();
|
|
m_participantDynamicData.clear();
|
|
m_participantGearEvents.clear();
|
|
m_lrfParticipantFakeGearEvents.clear();
|
|
m_ltdParticipantFakeGearEvents.clear();
|
|
m_unknownOriginatorsParticipant.clear();
|
|
m_participantWithMaybeMissingPartyId.clear();
|
|
m_unknownOriginatorsAes.clear();
|
|
m_aesItemView2PartyId.clear();
|
|
m_participantsWithProperties.clear();
|
|
}
|
|
|
|
bool Participant2SituationMap::mapObjectsClicked(const QVector<UniqueMapId> &uniqueMapIds)
|
|
{
|
|
if (!m_ppmParticipantInfoIfc && !m_ocParticipantInfoIfc)
|
|
return false;
|
|
|
|
const QVector<int32_t> &simIds = uniqueMapIds2SimIds(uniqueMapIds);
|
|
if (simIds.isEmpty())
|
|
return true;
|
|
if (m_ppmParticipantInfoIfc)
|
|
m_ppmParticipantInfoIfc->showParticipantInfoWindow(simIds);
|
|
//if (m_ocParticipantInfoIfc)
|
|
// m_ocParticipantInfoIfc->showOcParticipantInfoWindow(simIds);
|
|
return true;
|
|
}
|
|
|
|
bool Participant2SituationMap::unifiedMapObjectData(UniqueMapId uniqueMapId, UnifiedMapObjectData &umodOut) const
|
|
{
|
|
if (!m_ppmDtc)
|
|
return false;
|
|
|
|
int32_t simIdOut = -1;
|
|
if (!this->uniqueMapId2SimId(uniqueMapId, simIdOut))
|
|
return false;
|
|
|
|
auto it = m_participants.constFind(simIdOut);
|
|
if (it == m_participants.constEnd())
|
|
return false;
|
|
|
|
umodOut = UnifiedMapObjectData();
|
|
const ParticipantManagement::Participant &particpantData = it.value();
|
|
if (particpantData.equipmentNumber().is_set())
|
|
umodOut.m_equipmentNumber = particpantData.equipmentNumber().get();
|
|
umodOut.m_participantData.m_participantId = particpantData.id();
|
|
umodOut.m_participantData.m_typeId = particpantData.typeId();
|
|
umodOut.m_participantData.m_typeName = QString::fromStdString(m_ppmDtc->type(particpantData.typeId()).name());
|
|
const auto itDynData = m_participantDynamicData.find(particpantData.id());
|
|
if (itDynData != m_participantDynamicData.end())
|
|
{
|
|
const auto &dynData = itDynData.value();
|
|
if (dynData.stateHistory().size() != 0)
|
|
{
|
|
const ParticipantManagement::Type type = m_ppmDtc->type(particpantData.typeId());
|
|
const ParticipantManagement::TypeClass typeClass = type.typeClass();
|
|
const ProjectDataManagement::VulnerabilityClass vc = typeClass == ParticipantManagement::TypeClass_def::Human ?
|
|
ProjectDataManagement::VulnerabilityClass_def::SoldierInjuries :
|
|
ProjectDataManagement::VulnerabilityClass_def::VehicleDamages;
|
|
|
|
const ParticipantManagement::StateInformation &lastState = m_ppmDtc->youngestStateInformation(dynData.stateHistory());
|
|
const ProjectDataManagement::VulnerabilityState &vulState = lastState.vulnerabilityState();
|
|
umodOut.m_participantData.m_vulnerabilityState = vulState.underlying();
|
|
umodOut.m_participantData.m_vulnerabilityString = ProjectDataManagement::toString(vulState, vc);
|
|
}
|
|
}
|
|
|
|
umodOut.m_partyId = particpantData.partyId();
|
|
|
|
umodOut.m_description1 = QString::fromStdString(particpantData.designator());
|
|
umodOut.m_description2 = QString::fromStdString(particpantData.tacticalIdentifier());
|
|
|
|
RefPointer<SituationMap::Participant> mapObj = this->getMapObjectOfUniqueMapId(uniqueMapId);
|
|
if (mapObj.valid())
|
|
{
|
|
if (!mapObj->updateView())
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " unifiedMapObjectData: debugInfo='" << mapObj->debugInfo() << "'" << " updateView failed";
|
|
umodOut.m_symDataToRender = mapObj->symbolDataToRender();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Participant2SituationMap::enableFireEventFlashing(bool enable)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " enable: " << enable;
|
|
|
|
if (m_fireEventFlashEnable == enable)
|
|
return true;
|
|
|
|
if (!m_fireEventFlashTimer)
|
|
return false;
|
|
|
|
m_fireEventFlashEnable = enable;
|
|
if (m_fireEventFlashEnable)
|
|
m_fireEventFlashTimer->start();
|
|
else
|
|
{
|
|
m_fireEventFlashTimer->stop();
|
|
|
|
// set forced Color to an invalid color:
|
|
m_fireEventFlashTriState = 0;
|
|
updateGearEvents(true); // perform updateView immediately be ensure correct display for pause, hardcopy etc.
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " updateGearEvents done";
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Participant2SituationMap::isFireEventFlashingEnabled() const
|
|
{
|
|
return m_fireEventFlashEnable;
|
|
}
|
|
|
|
bool Participant2SituationMap::isParticipantVisible(const int32_t participantId, bool *bParticipantExists) const
|
|
{
|
|
RefPointer<MAP_T> mapObj = getMapObjectOfSimId(participantId);
|
|
if (!mapObj.valid())
|
|
{
|
|
if (bParticipantExists)
|
|
*bParticipantExists = false;
|
|
return false;
|
|
}
|
|
if (bParticipantExists)
|
|
*bParticipantExists = true;
|
|
return mapObj->visibility();
|
|
}
|
|
|
|
void Participant2SituationMap::onPartitionTimeChanged(qint64 newPartitionTime)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START, newPartitionTime=" << newPartitionTime
|
|
<< ", exerciseId = " << m_pluginProvider->exerciseId()
|
|
<< ", workspaceId = " << m_pluginProvider->workspaceId()
|
|
<< ", this=" << this;
|
|
|
|
QElapsedTimer timer;
|
|
if (m_logger.isTraceEnabled())
|
|
timer.start();
|
|
|
|
|
|
for (const InternalPosition &pos : qAsConst(m_participantPositions))
|
|
{
|
|
if (LtsUtils::isPositionTimeOutdated(newPartitionTime, pos.sampleTime()))
|
|
handleSymbolAppearanceSettingsParticipant(pos.participantId());
|
|
}
|
|
|
|
// MovementTrack:
|
|
QVector<int> simIds;
|
|
if (m_movementTrackManager)
|
|
{
|
|
m_movementTrackManager->setCurrentTimeMsec(newPartitionTime);
|
|
simIds = m_movementTrackManager->activeTrackIds();
|
|
|
|
for (int32_t simId : qAsConst(simIds))
|
|
{
|
|
RefPointer<MAP_T> mapObj = this->getMapObjectOfSimId(simId);
|
|
if (!mapObj.valid())
|
|
continue;
|
|
|
|
PositionCont movementTrack = m_movementTrackManager->activeAndVisibleTrack(simId);
|
|
|
|
if (movementTrack.isEmpty())
|
|
continue;
|
|
|
|
// Do not continue here
|
|
// if (movementTrack.last() == mapObj->origin())
|
|
// continue;
|
|
// The participant has to receive the recalculated track.
|
|
// (maybe reduced just by new partitionTime)
|
|
|
|
// add current origin only if different:
|
|
if (movementTrack.last() != mapObj->origin())
|
|
movementTrack.append(mapObj->origin());
|
|
|
|
mapObj->setMovementTrack(movementTrack);
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " movementTrack debugInfo='" << mapObj->debugInfo() << "'" << " update ..."
|
|
<< " movementTrack.size()=" << movementTrack.size();
|
|
|
|
addToUpdateNotify(mapObj);
|
|
} // for
|
|
|
|
} // if (m_movementTrackManager)
|
|
|
|
notifyMapAboutModifedObjects();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, newPartitionTime=" << newPartitionTime
|
|
<< ", took [ms] " << timer.elapsed()
|
|
<< ", exerciseId = " << m_pluginProvider->exerciseId()
|
|
<< ", workspaceId = " << m_pluginProvider->workspaceId()
|
|
<< ", ids.count()" << simIds.count()
|
|
<< " - finished";
|
|
}
|
|
|
|
void Participant2SituationMap::onFireEventFlashTimer()
|
|
{
|
|
// 0, 1, 2
|
|
++m_fireEventFlashTriState;
|
|
|
|
if (m_fireEventFlashTriState >= 3)
|
|
m_fireEventFlashTriState = 0;
|
|
|
|
this->updateGearEvents();
|
|
}
|
|
|
|
void Participant2SituationMap::onTypesChanged(const ParticipantManagement::Type &data, DataModification modType)
|
|
{
|
|
if (modType == DataModification::eRemoved)
|
|
{
|
|
m_types.remove(data.id());
|
|
return;
|
|
}
|
|
|
|
m_types[data.id()] = data;
|
|
updateHierarchyLevels();
|
|
}
|
|
|
|
void Participant2SituationMap::updateHierarchyLevels()
|
|
{
|
|
//
|
|
// ensure that the required layer exists (depending on the possible types/hierachyLevel)
|
|
//
|
|
if (m_layerFactory)
|
|
{
|
|
std::set<qint32> hLevels; // std::set is ordered, QSet not!!!!!!!!!!
|
|
for (const ParticipantManagement::Type &type : qAsConst(m_types))
|
|
hLevels.insert(type.hierarchyLevel());
|
|
|
|
|
|
RenderingMode renderingMode = m_layerFactory->participantLayerRenderingMode();
|
|
SurfacePlacement surfacePlacement = m_layerFactory->participantLayerSurfacePlacement();
|
|
|
|
|
|
for (const qint32 hLevel : hLevels)
|
|
{
|
|
const QString &layername = this->layernameWithHierachy(hLevel);
|
|
|
|
if (!m_layerFactory->hasOperationalLayer(layername))
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " create logicalLayer: '" << layername << "'"
|
|
<< " rm = " << renderingMode
|
|
<< " sp = " << surfacePlacement;
|
|
|
|
if (!m_layerFactory->addOperationalLayer(layername, tr("Participant (Level %1)").arg(hLevel), renderingMode, surfacePlacement)) // dt. Teilnehmer (Ebene 1) - compare map_initializer.cpp
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " failure on creating logicalLayer: '" << layername << "'";
|
|
}
|
|
else
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " already exists, logicalLayer: '" << layername << "'";
|
|
} // for
|
|
} // if (m_layerFactory)
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
void updateContainer(QHash<int, T> &cont, const T &data, int id, DataModification modType)
|
|
{
|
|
switch (modType)
|
|
{
|
|
case DataModification::eAdded:
|
|
case DataModification::eModified:
|
|
cont[id] = data;
|
|
break;
|
|
case DataModification::eRemoved:
|
|
cont.remove(id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Participant2SituationMap::onParticipantChanged(const ParticipantManagement::Participant &data,
|
|
DataModification modType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onParticipantChanged: " << data;
|
|
updateContainer(m_participants, data, data.id(), modType);
|
|
checkForUnknownOriginatorsParticipant(data.id());
|
|
if (handleChanged(data, modType) && modType != DataModification::eRemoved)
|
|
updateParticipantPosition(data.id()); // make sure to set the current position
|
|
|
|
// purpose is to get the value the porperty(is artillery or not) of the participant data. feuereinheit
|
|
const ParticipantManagement::ParticipantData &ppData = m_ppmDtc->convertParticipantToParticipantData(data);
|
|
const ParticipantManagement::Type &ppType = m_ppmDtc->type(ppData.typeId());
|
|
const QJsonObject typeProperties(QJsonDocument::fromJson(QByteArray::fromStdString(ppType.properties())).object());
|
|
updateContainer(m_participantsWithProperties, typeProperties[QStringLiteral("isArtillery")].toBool(false), data.id(), modType);
|
|
|
|
|
|
}
|
|
|
|
void Participant2SituationMap::onPositionChanged(const ParticipantManagement::ParticipantPosition &data,
|
|
DataModification modType)
|
|
{
|
|
TRACE_LOG(m_logger) << "onPositionChanged: " << data;
|
|
|
|
if(strcmp(PROJECTBUILD, "commaster") == 0 || strcmp(PROJECTBUILD, "demotablet") == 0)
|
|
{
|
|
//NOTE: Maybe faster if m_participants is used instead of dtc, but unsure if m_participants got all the participants at the right time
|
|
if(m_ppmDtc != nullptr)
|
|
{
|
|
auto participant = m_ppmDtc.get()->participant(data.participantId());
|
|
if(participant.equipmentNumber().is_set())
|
|
{
|
|
if(participant.equipmentNumber().get() == 1)
|
|
{
|
|
DEBUG_LOG(m_logger) << "ownPositionChanged2!!!";
|
|
const Point3D newPos(data.pos().lon(), data.pos().lat(), data.pos().z());
|
|
emit participantOwnPositionChanged(newPos, data.sampleTime());
|
|
|
|
return;
|
|
}
|
|
//NOTE: Check if upper hierarchy icon anf if true, ignore
|
|
//FIXME: Maybe not necessary if participant with EqNumber 1 is always in the highest hierarchy.
|
|
// if(participant.superiorId() != 0)
|
|
// {
|
|
// if(m_ppmDtc.get()->participant(participant.superiorId()).equipmentNumber().get())
|
|
// if(m_ppmDtc.get()->participant(participant.superiorId()).equipmentNumber().get() == 1)
|
|
// return;
|
|
//}
|
|
}
|
|
}
|
|
}
|
|
|
|
InternalPosition pos;
|
|
pos.participantId(data.participantId());
|
|
pos.positionLeader(data.pos());
|
|
pos.sampleTimeLeader(data.sampleTime());
|
|
pos.reportingSource = data.reportingSource();
|
|
pos.isUnit = false;
|
|
|
|
if (data.mountedOn().is_set() && data.mountedOn().get() > 0)
|
|
pos.bIsMountedOn = true;
|
|
|
|
if (data.mountedParticipants().is_set() && data.mountedParticipants().get().size() > 0)
|
|
pos.bHasMountedParticipants = true;
|
|
|
|
|
|
|
|
updateContainer(m_participantPositions, std::move(pos), data.participantId(), modType);
|
|
updateParticipantPosition(data.participantId());
|
|
}
|
|
|
|
void Participant2SituationMap::onUnitPositionChanged(const ParticipantManagement::UnitPosition &data,
|
|
DataModification modType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onUnitPositionChanged: " << data;
|
|
|
|
#ifdef GUEZ_MODE
|
|
if ( (m_pluginProvider->appId() == LtsAppDescriptor::App::OC) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->isTablet()) )
|
|
{
|
|
DEBUG_LOG(m_logger) << "OcMode: no Unit Pos Update" << data;
|
|
return;
|
|
}
|
|
#endif
|
|
InternalPosition pos(data, m_bShowCenterPositions);
|
|
updateContainer(m_participantPositions, pos, data.participantId(), modType);
|
|
|
|
updateParticipantPosition(pos);
|
|
}
|
|
|
|
void Participant2SituationMap::updateParticipantPosition(int participantId, bool bHandleSymbolAppearanceSettingsParticipant)
|
|
{
|
|
const auto itPos = m_participantPositions.constFind(participantId);
|
|
if (itPos == m_participantPositions.constEnd())
|
|
return;
|
|
updateParticipantPosition(itPos.value(), bHandleSymbolAppearanceSettingsParticipant);
|
|
}
|
|
|
|
void Participant2SituationMap::updateParticipantPosition(const InternalPosition &pos, bool bHandleSymbolAppearanceSettingsParticipant)
|
|
{
|
|
// Assume, that participant was already created:
|
|
const auto it = m_simId2UniqueMapId.constFind(pos.participantId());
|
|
if (it == m_simId2UniqueMapId.constEnd())
|
|
return;
|
|
|
|
const auto participantId = pos.participantId();
|
|
const UniqueMapId uniqueMapId = it.value();
|
|
RefPointer<Sim2SituationMap::MAP_T> mapObj = m_objFac.get<Sim2SituationMap::MAP_T>(uniqueMapId);
|
|
if (!mapObj)
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " not found in objFac"
|
|
<< " simId = " << participantId
|
|
<< " uniqueMapId = " << uniqueMapId;
|
|
}
|
|
|
|
if (hasGeoPositionNaN(pos.position()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "InternalPosition of participantId " << participantId
|
|
<< " has nan!"
|
|
<< " pos.positionCenter() = " << pos.positionCenter()
|
|
<< " pos.positionLeader() = " << pos.positionLeader();
|
|
if (mapObj->visibility())
|
|
{
|
|
mapObj->setVisibility(false); // shortcut
|
|
addToUpdateNotify(mapObj);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// IPS-Range / Location 'Tunnel' -> PositionReportingSource ==
|
|
QString str_X_altitudeDepth;
|
|
if (pos.reportingSource == Core::PositionReportingSource::OpsTunnel)
|
|
str_X_altitudeDepth = QStringLiteral("Tnnl");
|
|
|
|
if (mapObj->textModifier(Participant::TextModifier::XY_AltitudeLocation) != str_X_altitudeDepth)
|
|
{
|
|
mapObj->setTextModifier(Participant::TextModifier::XY_AltitudeLocation, str_X_altitudeDepth);
|
|
addToUpdateNotify(mapObj);
|
|
}
|
|
|
|
|
|
// has mounted participants? -> EF == '+'
|
|
QString strEF;
|
|
if (pos.bHasMountedParticipants)
|
|
strEF = QStringLiteral("+");
|
|
|
|
if (mapObj->textModifier(Participant::TextModifier::EF_ReinforcedReduced) != strEF)
|
|
{
|
|
mapObj->setTextModifier(Participant::TextModifier::EF_ReinforcedReduced, strEF);
|
|
addToUpdateNotify(mapObj);
|
|
}
|
|
|
|
// is mounted on another? suppress
|
|
if (pos.bIsMountedOn)
|
|
{
|
|
DEBUG_LOG(m_logger) << "participantId " << participantId
|
|
<< " is mounted on";
|
|
if (mapObj->visibility())
|
|
{
|
|
mapObj->setVisibility(false); // shortcut
|
|
addToUpdateNotify(mapObj);
|
|
}
|
|
// no return -> movementTrack will be updated: return; // return -> postionReport is not added to movementTrack (identical to kala code)
|
|
}
|
|
|
|
const Core::GeoPosition &geoPos = pos.position();
|
|
const Point3D newPos(geoPos.lon(), geoPos.lat(), geoPos.z());
|
|
|
|
// Position & Height:
|
|
bool positionChanged = mapObj->setOrigin(newPos);
|
|
if (positionChanged)
|
|
addToUpdateNotify(mapObj);
|
|
|
|
// MovementTrack:
|
|
if (m_movementTrackManager)
|
|
{
|
|
bool isTrackModified = m_movementTrackManager->addPosition(participantId, newPos, pos.sampleTime()); // pos will be added if track exists
|
|
|
|
if (isTrackModified)
|
|
{
|
|
const PositionCont &movementTrack = m_movementTrackManager->activeAndVisibleTrack(participantId);
|
|
mapObj->setMovementTrack(movementTrack);
|
|
}
|
|
} // if (m_movementTrackManager
|
|
|
|
if (bHandleSymbolAppearanceSettingsParticipant)
|
|
handleSymbolAppearanceSettingsParticipant(mapObj, m_participants.value(participantId));
|
|
|
|
if (positionChanged)
|
|
emit participantPositionChanged(participantId, newPos);
|
|
}
|
|
|
|
void Participant2SituationMap::onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data,
|
|
DataModification modType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onDynamicParticipantDataChanged: " << data;
|
|
|
|
const int32_t simId = data.participantId();
|
|
|
|
updateContainer(m_participantDynamicData, data, simId, modType);
|
|
|
|
// Assume, that participant was already created:
|
|
auto it = m_simId2UniqueMapId.constFind(simId);
|
|
if (it == m_simId2UniqueMapId.constEnd())
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " simId not found" << " simId = " << simId ;
|
|
return;
|
|
}
|
|
|
|
// retrieve mapObj
|
|
const UniqueMapId uniqueMapId = it.value();
|
|
RefPointer<Sim2SituationMap::MAP_T> mapObj = m_objFac.get<Sim2SituationMap::MAP_T>(uniqueMapId);
|
|
if (!mapObj)
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " not found in objFac"
|
|
<< " simId = " << simId
|
|
<< " uniqueMapId = " << uniqueMapId ;
|
|
return;
|
|
}
|
|
|
|
checkForUnknownOriginatorsParticipant(data.participantId());
|
|
|
|
// update dynamic data (State etc)
|
|
updateDynamicParticipantData(mapObj, data);
|
|
|
|
// refresh MapObjectSelectionWidget or similar:
|
|
emit mapObjectChanged(uniqueMapId);
|
|
}
|
|
|
|
void Participant2SituationMap::onBiteChanged(const ParticipantManagement::Bite &data, DataModification modType)
|
|
{
|
|
switch (modType)
|
|
{
|
|
case DataModification::eAdded:
|
|
case DataModification::eModified:
|
|
if (data.biteErrors().size() > 0)
|
|
m_hasBiteError.insert(data.participantId());
|
|
else
|
|
m_hasBiteError.remove(data.participantId());
|
|
break;
|
|
case DataModification::eRemoved:
|
|
m_hasBiteError.remove(data.participantId());
|
|
break;
|
|
}
|
|
handleSymbolAppearanceSettingsParticipant(data.participantId());
|
|
}
|
|
|
|
void Participant2SituationMap::onParticipantGearEventChanged(const ParticipantManagement::ParticipantGearEvent &data,
|
|
DataModification modType)
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " ParticipantGearEvent = "
|
|
<< " participantId= " << data.participantId()
|
|
<< " sampleTime= " << data.sampleTime();
|
|
|
|
// TODO: do we have to consider LRF and LTD charge types
|
|
// Assumption: no (Norbert Thoden)
|
|
|
|
switch (modType)
|
|
{
|
|
case DataModification::eAdded:
|
|
case DataModification::eModified:
|
|
{
|
|
m_participantGearEvents[data.participantId()] = data.sampleTime();
|
|
}
|
|
break;
|
|
|
|
// Is it correct to remove them?
|
|
// More or less irrelevant, since the topic is removed very late...
|
|
case DataModification::eRemoved:
|
|
{
|
|
const int participantId = data.participantId();
|
|
RefPointer<MAP_T> mapObj = this->getMapObjectOfSimId(participantId);
|
|
if (mapObj.valid())
|
|
mapObj->forcePartyColor(QColor()); // set invalid color to let the partyId take effect
|
|
|
|
m_participantGearEvents.remove(participantId);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Participant2SituationMap::onParticipantFakeGearEventChanged(int participantId, qint64 sampleTime, SpecialChargeType specialChargeType)
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " ParticipantGearEvent = "
|
|
<< ", participantId= " << participantId
|
|
<< ", sampleTime= " << sampleTime
|
|
<< ", specialChargeType= " << specialChargeType;
|
|
|
|
// Note:
|
|
// FakeEvent will not be removed from m_participant2SituationMap
|
|
// if there are two fake events: 1st at 10sec, 2nd at 20sec.
|
|
// the removeElement of the first must not remove the originatorParticipantId
|
|
// at m_participant2SituationMap
|
|
// Norbert Thoden, 2023-06-22
|
|
|
|
switch (specialChargeType)
|
|
{
|
|
case SpecialChargeType::LRF:
|
|
m_lrfParticipantFakeGearEvents[participantId] = sampleTime;
|
|
break;
|
|
case SpecialChargeType::LTD:
|
|
m_ltdParticipantFakeGearEvents[participantId] = sampleTime;
|
|
break;
|
|
case SpecialChargeType::NoneSpecial:
|
|
// empty
|
|
break;
|
|
} // switch
|
|
|
|
}
|
|
|
|
void Participant2SituationMap::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
|
|
{
|
|
// GUEZE-2717 3D Wirksim - EXC- Symbole Darstellung Karte
|
|
|
|
// SubOrdinate (nachrangige) AesItemViews are maybe removed from DDS.
|
|
// Like AesItemView with AtExplosionClusterGunToTargetLine.
|
|
// GunToTargetLine is replaced be real trajectory.
|
|
// So there is no more need for that topic -> AES is removing them from DDS.
|
|
|
|
static QVector<AreaEffectSimulation::AesType::inner_enum> subOrdinateAesTypeIds = {AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions,
|
|
AreaEffectSimulation::AesType_def::AtExplosionClusterSingleTrajectory,
|
|
AreaEffectSimulation::AesType_def::AtExplosionClusterGunToTargetLine};
|
|
|
|
if (subOrdinateAesTypeIds.contains(data.aesTypeId().underlying()))
|
|
return;
|
|
|
|
// nur für die vorrangingen AesTypeIds:
|
|
switch (modType)
|
|
{
|
|
case DataModification::eAdded:
|
|
case DataModification::eModified:
|
|
if (data.partyId().is_set())
|
|
m_aesItemView2PartyId[data.aesItemId()] = data.partyId().get();
|
|
else
|
|
m_aesItemView2PartyId.remove(data.aesItemId());
|
|
break;
|
|
case DataModification::eRemoved:
|
|
m_aesItemView2PartyId.remove(data.aesItemId());
|
|
break;
|
|
}
|
|
|
|
if (modType == DataModification::eRemoved)
|
|
return;
|
|
checkForUnknownOriginatorsAes(data.aesItemId()); // AesItemView::aesItemId() is entityId
|
|
}
|
|
|
|
void Participant2SituationMap::onDusMineStateChanged(const AreaEffectSimulation::DusMineState &/*data*/, DataModification /*modType*/)
|
|
{
|
|
auto copy = m_participantDynamicData;
|
|
for (const ParticipantManagement::DynamicParticipantData &dynPartData : qAsConst(copy))
|
|
{
|
|
if (!m_participantWithMaybeMissingPartyId.contains(dynPartData.participantId()))
|
|
continue;
|
|
onDynamicParticipantDataChanged(dynPartData, DataModification::eModified);
|
|
}
|
|
}
|
|
|
|
void Participant2SituationMap::onMovementTrackActivityChanged(int trackId, bool active)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START:"
|
|
<< " trackId=" << trackId
|
|
<< ", active=" << active;
|
|
|
|
if (!m_movementTrackManager)
|
|
return;
|
|
|
|
if (!m_pluginProvider->isSessionValid())
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " no m_session anymore: return";
|
|
return;
|
|
}
|
|
|
|
// simId == trackId;
|
|
|
|
RefPointer<MAP_T> mapObj = this->getMapObjectOfSimId(trackId);
|
|
if (!mapObj.valid())
|
|
return;
|
|
|
|
PositionCont movementTrack;
|
|
|
|
if (active)
|
|
{
|
|
movementTrack = m_movementTrackManager->activeAndVisibleTrack(trackId);
|
|
if (!movementTrack.isEmpty())
|
|
{
|
|
if (movementTrack.last() != mapObj->origin())
|
|
movementTrack.append(mapObj->origin());
|
|
}
|
|
else
|
|
{
|
|
// Track is very new, so set current origin as first position:
|
|
m_movementTrackManager->addPosition(trackId, mapObj->origin(), m_pluginProvider->partitionTime());
|
|
}
|
|
}
|
|
|
|
mapObj->setMovementTrack(movementTrack);
|
|
mapObj->setMovementTrackVisibility(active);
|
|
mapObj->setMovementTrackEnabled(active);
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << mapObj->debugInfo() << "'" << " simId/trackId=" << trackId;
|
|
|
|
addToUpdateNotify(mapObj);
|
|
|
|
//
|
|
// update GUI:
|
|
//
|
|
const QVector<int> &activeTrackIds = m_movementTrackManager->activeTrackIds(); // == active SimIds
|
|
m_movementTrackManager->emit_activeSimIdsChanged(activeTrackIds);
|
|
}
|
|
|
|
void Participant2SituationMap::onMovementTrackRemoved(int trackId)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << "simId/trackId=" << trackId;
|
|
|
|
if (!m_movementTrackManager)
|
|
return;
|
|
|
|
// simId == trackId;
|
|
|
|
RefPointer<MAP_T> mapObj = this->getMapObjectOfSimId(trackId);
|
|
if (!mapObj.valid())
|
|
return;
|
|
|
|
mapObj->setMovementTrack(PositionCont());
|
|
mapObj->setMovementTrackVisibility(false);
|
|
mapObj->setMovementTrackEnabled(false);
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << mapObj->debugInfo() << "'" << " simId/trackId=" << trackId;
|
|
|
|
if (!mapObj->updateView())
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << mapObj->debugInfo() << "'" << " updateView failed";
|
|
}
|
|
|
|
|
|
int Participant2SituationMap::participantHierachyLevel(const ParticipantManagement::Participant &data) const
|
|
{
|
|
const qint32 typeId = data.typeId();
|
|
auto it = m_types.find(typeId);
|
|
if (it == m_types.end())
|
|
return 0;
|
|
return it.value().hierarchyLevel();
|
|
}
|
|
|
|
|
|
QString Participant2SituationMap::layernameWithHierachy(qint32 hierachyLevel) const
|
|
{
|
|
if (hierachyLevel <= 0)
|
|
return m_layername;
|
|
return m_layername + QLatin1Char('_') + QString::number(hierachyLevel);
|
|
}
|
|
|
|
|
|
QString Participant2SituationMap::layernameWithHierachy(const Sim2SituationMap::DDS_T &ddsData) const
|
|
{
|
|
const qint32 hLevel = participantHierachyLevel(ddsData);
|
|
return layernameWithHierachy(hLevel);
|
|
}
|
|
|
|
bool Participant2SituationMap::isElapsed(const ParticipantManagement::Participant &data) const
|
|
{
|
|
|
|
bool isOwnPosition = false;
|
|
if(strcmp(PROJECTBUILD, "commaster") == 0 ||
|
|
strcmp(PROJECTBUILD, "demotablet") == 0)
|
|
{
|
|
if(data.equipmentNumber().is_set())
|
|
if(data.equipmentNumber().get() == 1)
|
|
isOwnPosition = true;
|
|
}
|
|
|
|
//NOTE: If the position is the ComMaster-Tablet Own Position -> Not show the Symbol on Map, instead a BlueDot will be shown
|
|
|
|
return data.isDeleted() || isOwnPosition;
|
|
}
|
|
|
|
|
|
void Participant2SituationMap::checkForUnknownOriginatorsParticipant(int possibleOriginatorParticipantId)
|
|
{
|
|
// m_unknownOriginatorsParticipant -> <originatorParticipantId, participantId>
|
|
if (m_unknownOriginatorsParticipant.isEmpty())
|
|
return;
|
|
auto cb = [this, possibleOriginatorParticipantId](QPair<int, int> p) -> bool
|
|
{
|
|
if (p.first != possibleOriginatorParticipantId)
|
|
return false;
|
|
auto it = m_participantDynamicData.constFind(p.second);
|
|
if (it != m_participantDynamicData.constEnd())
|
|
onDynamicParticipantDataChanged(it.value(), DataModification::eModified);
|
|
return true;
|
|
};
|
|
|
|
auto copy = m_unknownOriginatorsParticipant;
|
|
copy.erase(std::remove_if(copy.begin(), copy.end(), cb), copy.end());
|
|
std::swap(m_unknownOriginatorsParticipant, copy);
|
|
}
|
|
|
|
void Participant2SituationMap::checkForUnknownOriginatorsAes(int possibleOriginatorEffectSimId)
|
|
{
|
|
// m_unknownOriginatorsAes -> <originEffectSimulationId, participantId>
|
|
if (m_unknownOriginatorsAes.isEmpty())
|
|
return;
|
|
auto cb = [this, possibleOriginatorEffectSimId](QPair<int, int> p) -> bool
|
|
{
|
|
if (p.first != possibleOriginatorEffectSimId)
|
|
return false;
|
|
auto it = m_participantDynamicData.constFind(p.second);
|
|
if (it != m_participantDynamicData.constEnd())
|
|
onDynamicParticipantDataChanged(it.value(), DataModification::eModified);
|
|
return true;
|
|
};
|
|
|
|
auto copy = m_unknownOriginatorsAes;
|
|
copy.erase(std::remove_if(copy.begin(), copy.end(), cb), copy.end());
|
|
std::swap(m_unknownOriginatorsAes, copy);
|
|
}
|
|
|
|
bool Participant2SituationMap::updateDynamicParticipantData(RefPointer<Sim2SituationMap::MAP_T> mapObj,
|
|
const ParticipantManagement::DynamicParticipantData &data)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", data.participantId() " << data.participantId();
|
|
|
|
if (!mapObj)
|
|
return false;
|
|
|
|
// state/injury and it's injuryPartyId
|
|
SituationMap::Injury injury = SituationMap::Injury::NoInjury;
|
|
int32_t injuryPartyId = -1;
|
|
|
|
// state/injury:
|
|
const ParticipantManagement::StateInformation &lastState = m_ppmDtc->youngestStateInformation(data.stateHistory());
|
|
if (data.stateHistory().size() != 0)
|
|
{
|
|
const ProjectDataManagement::VulnerabilityState &vulState = lastState.vulnerabilityState();
|
|
switch (vulState.underlying()) // underlying -> inner_enum
|
|
{
|
|
case ProjectDataManagement::VulnerabilityState::CombatReady:
|
|
case ProjectDataManagement::VulnerabilityState::TemporarySeverance:
|
|
case ProjectDataManagement::VulnerabilityState::NearMiss:
|
|
case ProjectDataManagement::VulnerabilityState::HitWithNoEffect:
|
|
case ProjectDataManagement::VulnerabilityState::Reserved:
|
|
case ProjectDataManagement::VulnerabilityState::NoChange: //should never happen
|
|
injury = SituationMap::Injury::NoInjury;
|
|
break;
|
|
case ProjectDataManagement::VulnerabilityState::LightDamage:
|
|
case ProjectDataManagement::VulnerabilityState::MediumDamage:
|
|
case ProjectDataManagement::VulnerabilityState::HeavyDamage:
|
|
case ProjectDataManagement::VulnerabilityState::NotReady:
|
|
injury = SituationMap::Injury::Hit;
|
|
break;
|
|
case ProjectDataManagement::VulnerabilityState::MobilityKill:
|
|
case ProjectDataManagement::VulnerabilityState::VisibleMobilityKill:
|
|
injury = SituationMap::Injury::MobilityKill;
|
|
break;
|
|
case ProjectDataManagement::VulnerabilityState::WeaponSystemDamage:
|
|
case ProjectDataManagement::VulnerabilityState::VisibleWeaponSystemDamage:
|
|
injury = SituationMap::Injury::WeaponKill;
|
|
break;
|
|
case ProjectDataManagement::VulnerabilityState::CatastrophicKill:
|
|
injury = SituationMap::Injury::TotalKill;
|
|
break;
|
|
case ProjectDataManagement::VulnerabilityState::VulnerabilityBitsChanged:
|
|
case ProjectDataManagement::VulnerabilityState::NContamination:
|
|
case ProjectDataManagement::VulnerabilityState::BContamination:
|
|
case ProjectDataManagement::VulnerabilityState::CContamination:
|
|
// those four should no happen here
|
|
injury = SituationMap::Injury::Hit;
|
|
break;
|
|
} // switch
|
|
|
|
// injuryPartyId:
|
|
switch (lastState.originatorType().underlying())
|
|
{
|
|
case ProjectDataManagement::StateChangeOriginator::Participant:
|
|
{
|
|
const int32_t originParticipantId = lastState.originatorId();
|
|
if (originParticipantId != 0)
|
|
{
|
|
auto it = m_participants.constFind(originParticipantId);
|
|
if (it == m_participants.constEnd())
|
|
{
|
|
const ParticipantManagement::Participant &participantOrigin = m_ppmDtc->participant(originParticipantId);
|
|
if (participantOrigin.id() > 0) // It's a participant
|
|
{
|
|
m_participants.insert(participantOrigin.id(), participantOrigin);
|
|
injuryPartyId = participantOrigin.partyId();
|
|
const ParticipantManagement::ParticipantData &ppData = m_ppmDtc->convertParticipantToParticipantData(participantOrigin);
|
|
const ParticipantManagement::Type &ppType = m_ppmDtc->type(ppData.typeId());
|
|
const QJsonObject typeProperties(QJsonDocument::fromJson(QByteArray::fromStdString(ppType.properties())).object());
|
|
m_participantsWithProperties.insert(participantOrigin.id(), typeProperties[QStringLiteral("isArtillery")].toBool(false));
|
|
}
|
|
else if (originParticipantId != data.participantId()) // sanity
|
|
m_unknownOriginatorsParticipant.push_back(qMakePair(originParticipantId, data.participantId())); // originator is not know yet, store for delayed processing
|
|
|
|
}
|
|
else
|
|
injuryPartyId = it.value().partyId();
|
|
}
|
|
break;
|
|
}
|
|
case ProjectDataManagement::StateChangeOriginator::Party:
|
|
{
|
|
const ParticipantManagement::Party &partyOrigin = m_ppmDtc->party(lastState.originatorId());
|
|
if(partyOrigin.id())
|
|
injuryPartyId = partyOrigin.id();
|
|
break;
|
|
}
|
|
case ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation:
|
|
case ProjectDataManagement::StateChangeOriginator::FunctionalArea:
|
|
{
|
|
const int32_t originEffectSimulationId = lastState.originatorId();
|
|
|
|
if (originEffectSimulationId != 0)
|
|
{
|
|
// 1. try own hash (m_aesItemView2PartyId):
|
|
auto itAesItemView = m_aesItemView2PartyId.constFind(originEffectSimulationId);
|
|
if (itAesItemView == m_aesItemView2PartyId.constEnd())
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " aesItemViewFromEntityId failed 1, originEffectSimulationId = " << originEffectSimulationId
|
|
<< ", data.participantId() " << data.participantId();
|
|
|
|
#ifdef EXPENSIVE_FALLBACK
|
|
// August 2024, N. Thoden
|
|
// from my point of view, in this context, aesItemFromId() is expensive.
|
|
// It considers all different aesType (nContamination, bcContamination, explosionArea ...
|
|
// Additionally: enable..., waitForHistoricalData, and finally works on a QMap
|
|
|
|
// 2. try more expensive way:
|
|
// (more or less a fallback)
|
|
AreaEffectSimulation::AesItem aesItem;
|
|
m_aesDtc->aesItemFromId(originEffectSimulationId, aesItem);
|
|
if (aesItem.partyId().is_set())
|
|
{
|
|
injuryPartyId = aesItem.partyId().get();
|
|
|
|
#ifdef LTS_DEBUG
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " m_aesDtc->aesItemFromId , injuryPartyId = " << injuryPartyId
|
|
<< ", data.participantId() " << data.participantId()
|
|
<< " - modifiy the code to avoid using this fallback";
|
|
|
|
// Q_ASSERT_X(false, "Participant2SituationMap::updateDynamicParticipantData", "Using m_aesItemView2PartyId failed, but aesItemFromId works. Please reviewed.");
|
|
#else
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_aesDtc->aesItemFromId , injuryPartyId = " << injuryPartyId
|
|
<< ", data.participantId() " << data.participantId();
|
|
#endif // LTS_DEBUG
|
|
|
|
}
|
|
else
|
|
#endif // EXPENSIVE_FALLBACK
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " aesItemViewFromEntityId failed 2, originEffectSimulationId = " << originEffectSimulationId
|
|
<< ", data.participantId() " << data.participantId();
|
|
|
|
m_unknownOriginatorsAes.push_back(qMakePair(originEffectSimulationId, data.participantId())); // originator is not know yet, store for delayed processing
|
|
}
|
|
|
|
}
|
|
else
|
|
injuryPartyId = itAesItemView.value();
|
|
}
|
|
}
|
|
break;
|
|
case ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice:
|
|
{
|
|
auto guezIedDtc = m_pluginProvider->gimDtc();
|
|
if (guezIedDtc)
|
|
{
|
|
const GuezIedManagement::Ied &gIed = guezIedDtc->iedForEquipmentNumber(lastState.originatorId());
|
|
if (gIed.id() > 0)
|
|
injuryPartyId = gIed.partyId();
|
|
}
|
|
}
|
|
break;
|
|
case ProjectDataManagement::StateChangeOriginator::DusMine:
|
|
injuryPartyId = m_aesDtc->partyForDusMine(lastState.originatorId());
|
|
|
|
if (injuryPartyId == -1)
|
|
m_participantWithMaybeMissingPartyId.insert(data.participantId());
|
|
else
|
|
m_participantWithMaybeMissingPartyId.remove(data.participantId());
|
|
|
|
break;
|
|
case ProjectDataManagement::StateChangeOriginator::Unknown:
|
|
case ProjectDataManagement::StateChangeOriginator::ObserverController:
|
|
case ProjectDataManagement::StateChangeOriginator::ExerciseControl:
|
|
case ProjectDataManagement::StateChangeOriginator::Initialization:
|
|
{
|
|
injuryPartyId = 1; // 1 == OC
|
|
}
|
|
break;
|
|
case ProjectDataManagement::StateChangeOriginator::WoundTreatmentDevice:
|
|
case ProjectDataManagement::StateChangeOriginator::MaintainanceDevice:
|
|
case ProjectDataManagement::StateChangeOriginator::MedicalTimeout:
|
|
case ProjectDataManagement::StateChangeOriginator::Sanitation:
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " originator state change for " << lastState.originatorType() << " not (yet) implemented"
|
|
<< ", participantId = " << data.participantId()
|
|
<< ", sampleTime=" << data.sampleTime()
|
|
<< ", eqNumber=" << m_participants.value(data.participantId()).equipmentNumber()
|
|
<< ", mapObj->debugInfo() = " << mapObj->debugInfo();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
injury = SituationMap::Injury::NoInjury;
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " injuryPartyId = " << injuryPartyId
|
|
<< ", data.participantId() " << data.participantId();
|
|
|
|
|
|
// Maybe ParticipantPositions were received BEFORE DynamicParticipantData (like at Snapshot)
|
|
// So make sure to set the current position
|
|
updateParticipantPosition(data.participantId(), false);
|
|
|
|
|
|
// Hitline :
|
|
if ((injury != Injury::NoInjury) &&
|
|
(injuryPartyId >= 0) &&
|
|
(lastState.originatorType() == ProjectDataManagement::StateChangeOriginator::Participant))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Hit Event: "
|
|
<< " stateTime=" << lastState.stateTime()
|
|
<< " injury=" << injury
|
|
<< " injuryPartyId=" << injuryPartyId
|
|
<< " originatorType=" << lastState.originatorType()
|
|
<< " originatorChargeTypeId=" << lastState.originatorChargeTypeId();
|
|
|
|
//
|
|
// assume that 'position' is published BEFORE 'state'!!!
|
|
//
|
|
|
|
// retrieve very new data here (and not within the slot), to ensure consistent data:
|
|
|
|
HitlineInfo hlInfo;
|
|
hlInfo.participantId = data.participantId();
|
|
hlInfo.participantPosition = m_ppmDtc->participantPosition(data.participantId());
|
|
hlInfo.participantState = lastState;
|
|
hlInfo.originatorPosition = m_ppmDtc->participantPosition(lastState.originatorId());
|
|
hlInfo.injuryPartyId = injuryPartyId;
|
|
|
|
emit hitlineOccured(hlInfo);
|
|
}
|
|
// : Hitline
|
|
|
|
const bool bInjuryChanged = mapObj->setInjury(injury, injuryPartyId);
|
|
|
|
// Additional symbol for nbc protection without filter setting?
|
|
const bool bAddSymbolChanged = mapObj->setAdditionalSymbol(SituationMap::AdditionalSymbolType::NbcProtection, data.hasNbcProtectionActive() || data.hasNbcProtectionConfigured());
|
|
|
|
// Gear Ranges :
|
|
// Ammo can be stored at turret and/or tank
|
|
// data.gear contains that
|
|
// [NLDEVCTRL-926] avoid duplicates:
|
|
QVector<QPair<int, int>> validGearCharges;
|
|
validGearCharges.reserve(data.gear().size());
|
|
for (auto const &gear : data.gear())
|
|
{
|
|
QPair<int, int> candidate(gear.gearTypeId(), gear.chargeTypeId());
|
|
if (!validGearCharges.contains(candidate))
|
|
validGearCharges.append(candidate);
|
|
} // for
|
|
// now duplicates should be removed
|
|
|
|
QVector<int> gearRanges;
|
|
gearRanges.reserve(data.gear().size());
|
|
for (const QPair<int, int> &gearCharge : qAsConst(validGearCharges))
|
|
{
|
|
const int range = m_pdmHandler->maxRangeFromGearTypeIdAndChargeTypeId(gearCharge.first, gearCharge.second);
|
|
if (range > 0)
|
|
gearRanges.append(range);
|
|
} // for
|
|
|
|
const bool bRangesChanged = mapObj->setRanges(gearRanges);
|
|
// : Gear Ranges
|
|
|
|
// Designation :
|
|
const std::vector<ParticipantManagement::LaserDesignation> &designations = data.designations();
|
|
|
|
// convert data be transfered in via a single call: (easier to handle at ParticipantImpl)
|
|
QVector<Participant::DesignationData> designationDataVec;
|
|
designationDataVec.reserve(designations.size());
|
|
|
|
for (const ParticipantManagement::LaserDesignation &des : qAsConst(designations))
|
|
{
|
|
Participant::DesignationData designationData;
|
|
const Core::GeoPosition &spotLocation = des.spotLocation();
|
|
designationData.spotLocation.rx() = spotLocation.lon();
|
|
designationData.spotLocation.ry() = spotLocation.lat();
|
|
designationData.spotLocation.rz() = spotLocation.z();
|
|
if (designationData.spotLocation.isNull()) // invalid: continue -> will not be shonw on map display
|
|
continue;
|
|
designationData.code = des.code();
|
|
designationDataVec.push_back(designationData);
|
|
}
|
|
const bool bDesignationChanged = mapObj->setDesignations(designationDataVec);
|
|
// : Designation
|
|
|
|
if (bInjuryChanged || bAddSymbolChanged || bRangesChanged || bDesignationChanged)
|
|
addToUpdateNotify(mapObj);
|
|
|
|
// at last, is calling updateView
|
|
auto itPart = m_participants.constFind(data.participantId());
|
|
handleSymbolAppearanceSettingsParticipant(mapObj,
|
|
itPart == m_participants.constEnd() ? ParticipantManagement::Participant() : itPart.value());
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", data.participantId()" << data.participantId();
|
|
return true;
|
|
}
|
|
|
|
bool Participant2SituationMap::updateElementImpl(const ParticipantManagement::Participant &ddsData,
|
|
RefPointer<SituationMap::Participant> mapObj)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< " participant = " << participantToString(ddsData) ;
|
|
// SIDC:
|
|
// (role is not neccessary here, participant will take it from partyId via partyIdToColorFunctor)
|
|
SymbolData symData = mapObj->symbol();
|
|
symData.sidc = QString::fromStdString(ddsData.symbol());
|
|
mapObj->setSymbol(symData);
|
|
|
|
// PartyId:
|
|
mapObj->setPartyId(ddsData.partyId());
|
|
|
|
// TextModifier:
|
|
mapObj->setTextModifier(Participant::TextModifier::T_UniqueDesignation, QString::fromStdString(ddsData.designator()));
|
|
mapObj->setTextModifier(Participant::TextModifier::M_HigherFormation, QString::fromStdString(ddsData.tacticalIdentifier()));
|
|
|
|
const int32_t participantId = ddsData.id();
|
|
// process DynamicData:
|
|
auto itDpd = m_participantDynamicData.constFind(participantId);
|
|
if (itDpd != m_participantDynamicData.cend())
|
|
updateDynamicParticipantData(mapObj, itDpd.value());
|
|
else
|
|
// update symbol here if no m_participantDynamicData for participantId
|
|
handleSymbolAppearanceSettingsParticipant(participantId);
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
bool Participant2SituationMap::removeElement(const int32_t simId)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " simId=" << simId;
|
|
|
|
// to reduce the amount of calls to updateView(), the mapObj are stored
|
|
// at m_objectsToUpdate.
|
|
// If an object is removed by removeing the topic from DDS,
|
|
// Sim2SituationMap::removeElement is called.
|
|
// Overload this method to keep m_objectsToUpdate up-to-date:
|
|
|
|
const UniqueMapId uniqueMapId = m_simId2UniqueMapId.value(simId, INVALID_MAP_ID);
|
|
if (!isUniqueMapIdValid(uniqueMapId))
|
|
{
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " not found"
|
|
<< ", simId = " << simId;
|
|
return false;
|
|
}
|
|
|
|
QMutableVectorIterator<RefPointer<MAP_T>> it(m_objectsToUpdate);
|
|
while (it.hasNext())
|
|
{
|
|
const RefPointer<MAP_T> &item = it.next();
|
|
if (item->uniqueMapId() == uniqueMapId)
|
|
it.remove();
|
|
} // while
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " simId=" << simId;
|
|
return BaseClass::removeElement(simId);
|
|
}
|
|
|
|
void Participant2SituationMap::updateGearEvents(bool doUpdateView) // to let the participant flash
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< ", m_participantGearEvents.size()" << m_participantGearEvents.size()
|
|
<< ", doUpdateView=" << doUpdateView;
|
|
|
|
if (m_participantGearEvents.isEmpty()
|
|
&& m_lrfParticipantFakeGearEvents.isEmpty()
|
|
&& m_ltdParticipantFakeGearEvents.isEmpty() )
|
|
return;
|
|
|
|
if (!m_pluginProvider->isSessionValid())
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " no m_session anymore: return";
|
|
return;
|
|
}
|
|
|
|
const qint64 curPartitionTimeMSec = m_pluginProvider->partitionTime(); // msec
|
|
const qint64 fireEventFlashingDurationMSec = m_currentSymbolSettings.symbolSettings.symbolFireEventFlashingMSec; // msec
|
|
|
|
//
|
|
// real work:
|
|
//
|
|
|
|
QSet<int> particpantIdsWithGearEvents;
|
|
particpantIdsWithGearEvents.reserve(m_participantGearEvents.size() + m_lrfParticipantFakeGearEvents.size() + m_ltdParticipantFakeGearEvents.size());
|
|
|
|
particpantIdsWithGearEvents += QSet<int>(m_participantGearEvents.keyBegin() , m_participantGearEvents.keyEnd());
|
|
particpantIdsWithGearEvents += QSet<int>(m_lrfParticipantFakeGearEvents.keyBegin(), m_lrfParticipantFakeGearEvents.keyEnd());
|
|
particpantIdsWithGearEvents += QSet<int>(m_ltdParticipantFakeGearEvents.keyBegin(), m_ltdParticipantFakeGearEvents.keyEnd());
|
|
|
|
updateGearEventInternal(doUpdateView,
|
|
curPartitionTimeMSec, fireEventFlashingDurationMSec,
|
|
particpantIdsWithGearEvents);
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
void Participant2SituationMap::updateGearEventInternal(bool doUpdateView,
|
|
qint64 curPartitionTimeMSec,
|
|
qint64 fireEventFlashingDurationMSec,
|
|
const QSet<int> &particpantIdsWithGearEvents)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< " curPartitionTimeMSec =" << curPartitionTimeMSec
|
|
<< " fireEventFlashingDurationMSec =" << fireEventFlashingDurationMSec;
|
|
|
|
if (particpantIdsWithGearEvents.isEmpty())
|
|
return;
|
|
|
|
for (auto partId : qAsConst(particpantIdsWithGearEvents))
|
|
{
|
|
const UniqueMapId uniqueMapId = m_simId2UniqueMapId.value(partId, INVALID_MAP_ID);
|
|
if (uniqueMapId == INVALID_MAP_ID)
|
|
continue;
|
|
RefPointer<MAP_T> mapObj = m_objFac.get<MAP_T>(uniqueMapId);
|
|
if (!mapObj.valid())
|
|
continue;
|
|
|
|
// real work:
|
|
|
|
bool bFlash = false;
|
|
// typical GearEvents:
|
|
{
|
|
auto it = m_participantGearEvents.find(partId);
|
|
if (it != m_participantGearEvents.end())
|
|
{
|
|
const qint64 lastFireEventMSec = it.value();
|
|
|
|
// (aktuelle Zeit - Zeit letztes FeuerEreignis) > Settings.symbolFireEventFlashingMSec
|
|
const qint64 ageMSec = (curPartitionTimeMSec - lastFireEventMSec);
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " ageMSec = " << ageMSec << ", fireEventFlashingDurationMSec " << fireEventFlashingDurationMSec;
|
|
if (ageMSec <= fireEventFlashingDurationMSec)
|
|
{
|
|
// INFO_LOG(m_logger) << LOGFUNCTION << " flash caused by normal ammo (no LRF, LTD)";
|
|
bFlash = true;
|
|
}
|
|
|
|
// cleanup container (to avoid unnecessary updateView())
|
|
if (ageMSec > (120 + 10) * 1000) // GUI: maximum is 120sec
|
|
{
|
|
m_participantGearEvents.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
// faked GearEvents: LRF
|
|
if (!bFlash)
|
|
{
|
|
const int displayDuration = m_symbolAppearanceSettings->settings().symbolSettings.lrfLineDisplayLengthMSec;
|
|
if (displayDuration > 0) // if slider '0' -> do not show the line
|
|
{
|
|
auto it = m_lrfParticipantFakeGearEvents.find(partId);
|
|
if (it != m_lrfParticipantFakeGearEvents.end())
|
|
{
|
|
const qint64 lastFireEventMSec = it.value();
|
|
|
|
// (aktuelle Zeit - Zeit letztes FeuerEreignis) > Settings.symbolFireEventFlashingMSec
|
|
const qint64 ageMSec = (curPartitionTimeMSec - lastFireEventMSec);
|
|
if (ageMSec <= fireEventFlashingDurationMSec)
|
|
{
|
|
// INFO_LOG(m_logger) << LOGFUNCTION << " flash caused by LRF";
|
|
bFlash = true;
|
|
}
|
|
|
|
// cleanup container (to avoid unnecessary updateView())
|
|
if (ageMSec > (120 + 10) * 1000) // GUI: maximum is 120sec
|
|
{
|
|
m_lrfParticipantFakeGearEvents.erase(it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// faked GearEvents: LTD
|
|
if (!bFlash)
|
|
{
|
|
const int displayDuration = m_symbolAppearanceSettings->settings().symbolSettings.ltdLineDisplayLengthMSec;
|
|
if (displayDuration > 0) // if slider '0' -> do not show the line
|
|
{
|
|
auto it = m_ltdParticipantFakeGearEvents.find(partId);
|
|
if (it != m_ltdParticipantFakeGearEvents.end())
|
|
{
|
|
const qint64 lastFireEventMSec = it.value();
|
|
|
|
// (aktuelle Zeit - Zeit letztes FeuerEreignis) > Settings.symbolFireEventFlashingMSec
|
|
const qint64 ageMSec = (curPartitionTimeMSec - lastFireEventMSec);
|
|
if (ageMSec <= fireEventFlashingDurationMSec)
|
|
{
|
|
// INFO_LOG(m_logger) << LOGFUNCTION << " flash caused by LTD";
|
|
bFlash = true;
|
|
}
|
|
|
|
// cleanup container (to avoid unnecessary updateView())
|
|
if (ageMSec > (120 + 10) * 1000) // GUI: maximum is 120sec
|
|
{
|
|
m_ltdParticipantFakeGearEvents.erase(it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION
|
|
<< " curPartitionTimeMSec =" << curPartitionTimeMSec
|
|
<< " fireEventFlashingDurationMSec =" << fireEventFlashingDurationMSec
|
|
<< " bFlash =" << bFlash;
|
|
|
|
if (bFlash)
|
|
{
|
|
// flashing
|
|
switch (m_fireEventFlashTriState)
|
|
{
|
|
case 0:
|
|
default:
|
|
mapObj->forcePartyColor(QColor()); // set invalid color to let the partyId take effect
|
|
break;
|
|
case 1:
|
|
mapObj->forcePartyColor(Qt::white);
|
|
break;
|
|
case 2:
|
|
mapObj->forcePartyColor(Qt::gray);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// not flashing
|
|
mapObj->forcePartyColor(QColor()); // set invalid color to let the partyId take effect
|
|
}
|
|
|
|
if (doUpdateView)
|
|
{
|
|
if (!mapObj->updateView())
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << mapObj->debugInfo() << "'" << " updateView failed";
|
|
}
|
|
else
|
|
addToUpdateNotify(mapObj);
|
|
|
|
} // for
|
|
|
|
INFO_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
|
|
// function name is not really optimal ...
|
|
// @return true when an additional symbol should be displayed, otherwise false
|
|
static inline bool symbolVisbilityHelper(FilterManagement::SymbolAppearanceSettings::SymbolVisibility &curSv,
|
|
FilterManagement::SymbolAppearanceSettings::SymbolVisibility requestedSv,
|
|
bool bActive)
|
|
{
|
|
if (!bActive)
|
|
return false;
|
|
|
|
// Standard < Faded < Hidden
|
|
switch (requestedSv)
|
|
{
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::AdditionalSymbol:
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Standard:
|
|
// curSv = curSv;
|
|
break;
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Faded:
|
|
curSv = (curSv == FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Hidden) ? curSv : requestedSv;
|
|
break;
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Hidden:
|
|
curSv = requestedSv;
|
|
break;
|
|
}
|
|
return requestedSv == FilterManagement::SymbolAppearanceSettings::SymbolVisibility::AdditionalSymbol;
|
|
}
|
|
|
|
void Participant2SituationMap::handleSymbolAppearanceSettings(RefPointer<MAP_T> &mapObj)
|
|
{
|
|
Q_UNUSED(mapObj);
|
|
// Don`t use BaseClass implementation, handle this on our own
|
|
}
|
|
|
|
void Participant2SituationMap::onSymbolAppearanceSettingsChanged(FilterManagement::SymbolAppearanceSettings::Part part)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< ", appid=" << int(m_pluginProvider->appId())
|
|
<< ", workspaceId=" << m_pluginProvider->workspaceId()
|
|
<< ", part=" << static_cast<int>(part)
|
|
<< ", this=" << this;
|
|
|
|
Q_UNUSED(part);
|
|
// Don`t use BaseClass implementation, handle this on our own:
|
|
|
|
if (!m_symbolAppearanceSettings)
|
|
return;
|
|
|
|
// TODO: check what really changed and only update this -> remember last filter and compare values
|
|
// for now: only testing ...
|
|
const bool relevant = symbolAppearanceSettingsChangesAreRelevant(m_symbolAppearanceSettings->settings());
|
|
|
|
m_currentSymbolSettings = m_symbolAppearanceSettings->settings();
|
|
|
|
const bool bShowCenterPosNew =
|
|
m_currentSymbolSettings.symbolSettings.unitSymbolPosition == FilterManagement::SymbolAppearanceSettings::UnitSymbolPosition::Center;
|
|
if (bShowCenterPosNew != m_bShowCenterPositions)
|
|
{
|
|
m_bShowCenterPositions = bShowCenterPosNew;
|
|
for (auto it = m_participantPositions.begin(); it != m_participantPositions.end(); ++it)
|
|
{
|
|
InternalPosition &pos = it.value();
|
|
if (!pos.isUnit)
|
|
continue;
|
|
pos.bShowCenter = m_bShowCenterPositions;
|
|
updateParticipantPosition(pos);
|
|
}
|
|
}
|
|
|
|
if (!relevant)
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " not relevant, skip";
|
|
return;
|
|
}
|
|
|
|
//
|
|
// real work:
|
|
//
|
|
|
|
// MovementTrack:
|
|
if (m_movementTrackManager)
|
|
{
|
|
int movementTrackLengthMSec = m_currentSymbolSettings.symbolSettings.movementTrackLengthMSec;
|
|
m_movementTrackManager->setMaxViewAgeMsec(movementTrackLengthMSec);
|
|
|
|
const QVector<int> &simIds = m_movementTrackManager->activeTrackIds();
|
|
for (int simId : simIds)
|
|
{
|
|
RefPointer<MAP_T> mapObj = this->getMapObjectOfSimId(simId);
|
|
if (!mapObj.valid())
|
|
continue;
|
|
const PositionCont &movementTrack = m_movementTrackManager->activeAndVisibleTrack(simId);
|
|
mapObj->setMovementTrack(movementTrack);
|
|
|
|
addToUpdateNotify(mapObj);
|
|
} // for
|
|
}
|
|
|
|
|
|
for (const ParticipantManagement::Participant &participant : qAsConst(m_participants))
|
|
{
|
|
const UniqueMapId uniqueMapId = m_simId2UniqueMapId.value(participant.id(), INVALID_MAP_ID);
|
|
if (uniqueMapId == INVALID_MAP_ID)
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << "Found a participant which is in internal map but has no map object...: " << participant.id();
|
|
continue;
|
|
}
|
|
RefPointer<MAP_T> mapObj = m_objFac.get<MAP_T>(uniqueMapId);
|
|
if (!mapObj.valid())
|
|
continue;
|
|
|
|
handleSymbolAppearanceSettingsParticipant(mapObj, participant);
|
|
}
|
|
|
|
// apply filter directly
|
|
notifyMapAboutModifedObjects();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
|
|
<< ", appid=" << int(m_pluginProvider->appId())
|
|
<< ", workspaceId=" << m_pluginProvider->workspaceId()
|
|
<< ", part=" << static_cast<int>(part)
|
|
<< ", this=" << this;
|
|
}
|
|
|
|
static inline bool hasManipulation(const ParticipantManagement::DynamicParticipantData &dpd)
|
|
{
|
|
return dpd.manipulation().size() > 0;
|
|
}
|
|
|
|
bool Participant2SituationMap::hasBitError(const ParticipantManagement::DynamicParticipantData &dpd) const
|
|
{
|
|
return m_hasBiteError.contains(dpd.participantId());
|
|
}
|
|
|
|
bool Participant2SituationMap::isSimulatedPosition(const InternalPosition &pos) const
|
|
{
|
|
return (pos.reportingSource == Core::PositionReportingSource::MovementSimulation ||
|
|
pos.reportingSource == Core::PositionReportingSource::SetByOperator);
|
|
}
|
|
|
|
bool Participant2SituationMap::isOutOfDate(const InternalPosition &pos) const
|
|
{
|
|
if (!m_pluginProvider->isSessionValid())
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " no m_session anymore: return";
|
|
return false;
|
|
}
|
|
return LtsUtils::isPositionTimeOutdated(m_pluginProvider->partitionTime(), pos.sampleTime());
|
|
}
|
|
|
|
void Participant2SituationMap::handleSymbolAppearanceSettingsParticipant(int participantId)
|
|
{
|
|
const UniqueMapId uniqueMapId = m_simId2UniqueMapId.value(participantId, INVALID_MAP_ID);
|
|
if (uniqueMapId == INVALID_MAP_ID)
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " Got a participant which is in internal map but has no map object...: " << participantId;
|
|
return;
|
|
}
|
|
RefPointer<MAP_T> mapObj = m_objFac.get<MAP_T>(uniqueMapId);
|
|
if (!mapObj.valid())
|
|
return;
|
|
if (!m_symbolAppearanceSettings)
|
|
return;
|
|
const auto itPart = m_participants.constFind(participantId);
|
|
handleSymbolAppearanceSettingsParticipant(mapObj,
|
|
itPart == m_participants.constEnd() ? ParticipantManagement::Participant() : itPart.value());
|
|
}
|
|
|
|
void Participant2SituationMap::handleSymbolAppearanceSettingsParticipant(RefPointer<MAP_T> &mapObj,
|
|
const ParticipantManagement::Participant &participant)
|
|
{
|
|
const auto itDpd = m_participantDynamicData.constFind(participant.id());
|
|
const auto itPos = m_participantPositions.constFind(participant.id());
|
|
handleSymbolAppearanceSettingsParticipant(mapObj,
|
|
participant,
|
|
itPos == m_participantPositions.cend() ? InternalPosition() : itPos.value(),
|
|
itDpd == m_participantDynamicData.cend() ? ParticipantManagement::DynamicParticipantData() : itDpd.value());
|
|
}
|
|
|
|
|
|
void Participant2SituationMap::handleSymbolAppearanceSettingsParticipant(RefPointer<MAP_T> &mapObj,
|
|
const ParticipantManagement::Participant &participant,
|
|
const InternalPosition &pPos,
|
|
const ParticipantManagement::DynamicParticipantData &dpd)
|
|
{
|
|
if (!m_symbolAppearanceSettings)
|
|
{
|
|
addToUpdateNotify(mapObj);
|
|
return;
|
|
}
|
|
|
|
const FilterManagement::SymbolAppearanceSettings::Settings &s = m_symbolAppearanceSettings->settings();
|
|
|
|
const auto l = s.symbolSettings.symbolPartyAppearance.value(participant.partyId());
|
|
mapObj->setTextModifierVisibility(Participant::TextModifier::T_UniqueDesignation, l.showDesignator);
|
|
mapObj->setTextModifierVisibility(Participant::TextModifier::M_HigherFormation, l.showTacticalIdentifier);
|
|
mapObj->setUserSymbolOpacity(1.0f - s.symbolSettings.symbolTransparency);
|
|
mapObj->setScale(s.symbolSettings.symbolScale);
|
|
|
|
bool bIsChanged = false;
|
|
|
|
// combine the four visibility to current visibility
|
|
FilterManagement::SymbolAppearanceSettings::SymbolVisibility sv = FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Standard;
|
|
bIsChanged |= mapObj->setAdditionalSymbol(SituationMap::AdditionalSymbolType::BiteError, symbolVisbilityHelper(sv, s.symbolSettings.bitErrorVisibility, hasBitError(dpd)));
|
|
bIsChanged |= mapObj->setAdditionalSymbol(SituationMap::AdditionalSymbolType::Manipulation, symbolVisbilityHelper(sv, s.symbolSettings.manipulationVisibility, hasManipulation(dpd)));
|
|
bIsChanged |= mapObj->setAdditionalSymbol(SituationMap::AdditionalSymbolType::NoPosition, symbolVisbilityHelper(sv, s.symbolSettings.positionOutOfDateVisibility, isOutOfDate(pPos)));
|
|
bIsChanged |= mapObj->setMarked(symbolVisbilityHelper(sv, s.symbolSettings.simulatedPositionVisibility, isSimulatedPosition(pPos)));
|
|
|
|
bool bSymbolVisible = true;
|
|
bool bSymbolIsFaded = false;
|
|
switch (sv)
|
|
{
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Standard:
|
|
bSymbolVisible = true;
|
|
bSymbolIsFaded = false;
|
|
break;
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Faded:
|
|
bSymbolVisible = true;
|
|
bSymbolIsFaded = true;
|
|
break;
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::Hidden:
|
|
bSymbolVisible = false;
|
|
bSymbolIsFaded = false;
|
|
break;
|
|
case FilterManagement::SymbolAppearanceSettings::SymbolVisibility::AdditionalSymbol:
|
|
// should never happen, change nothing
|
|
break;
|
|
}
|
|
|
|
if (!bSymbolVisible && s.symbolSettings.showFiringUnits && m_participantsWithProperties.value(participant.id(), false))
|
|
bSymbolVisible = true;
|
|
|
|
if (!l.isVisible)
|
|
bSymbolVisible = false;
|
|
if (bSymbolVisible && !m_pluginProvider->isVisibleParty(participant.partyId()))
|
|
bSymbolVisible = false;
|
|
if (bSymbolVisible && s.hiddenTypeFilter.value(participant.partyId()).contains(participant.typeId()))
|
|
bSymbolVisible = false;
|
|
if (bSymbolVisible && hasGeoPositionNaN(pPos.position()))
|
|
bSymbolVisible = false;
|
|
if (bSymbolVisible && pPos.bIsMountedOn)
|
|
bSymbolVisible = false;
|
|
|
|
if (bSymbolVisible)
|
|
{
|
|
const auto youngestState = m_ppmDtc->youngestStateInformation(dpd.stateHistory());
|
|
if (s.hiddenStateFilter.value(participant.partyId()).contains(youngestState.vulnerabilityState().underlying()))
|
|
bSymbolVisible = false;
|
|
}
|
|
if (bSymbolVisible && m_symbolAppearanceSettings->isParticipantVisibileCollapseExpand(participant.id()))
|
|
bSymbolVisible = false;
|
|
|
|
bool oldVisibility = mapObj->visibility();
|
|
mapObj->setVisibility(bSymbolVisible);
|
|
mapObj->setUseLightColor(bSymbolIsFaded);
|
|
|
|
// TODO: only call when really something changed
|
|
|
|
// only update if mapObj is visible or becomes visible due to recent changes
|
|
if (oldVisibility || bSymbolVisible)
|
|
addToUpdateNotify(mapObj);
|
|
|
|
|
|
// refresh MapObjectSelectionWidget or similar:
|
|
if (bIsChanged)
|
|
emit mapObjectChanged(mapObj->uniqueMapId());
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " Symbol visibilty for Entity: " << mapObj->debugInfo()
|
|
<< " with participant.id " << participant.id()
|
|
<< " and partyId " << participant.partyId()
|
|
<< " is " << bSymbolVisible;
|
|
}
|
|
|
|
void Participant2SituationMap::addToUpdateNotify(const RefPointer<MAP_T> &mapObj)
|
|
{
|
|
if (mapObj)
|
|
m_objectsToUpdate += mapObj;
|
|
}
|
|
|
|
void Participant2SituationMap::notifyMapAboutModifedObjects()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START, m_objectsToUpdate.size() = " << m_objectsToUpdate.size() << ", this=" << this;
|
|
for (RefPointer<MAP_T> mapObj : qAsConst(m_objectsToUpdate))
|
|
{
|
|
if (mapObj)
|
|
mapObj->updateView();
|
|
}
|
|
m_objectsToUpdate.clear();
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", this=" << this;
|
|
}
|
|
|
|
} // namespace SituationMap
|