Migrate BuildingViewer (16 files: DDS members, m_logger inits, QOpenGLWidget); gui:3:bld.info + gui:3:bdv now load
1611 lines
60 KiB
C++
1611 lines
60 KiB
C++
|
|
|
|
#include "bdv_dds_controller.h"
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
|
|
#include "bdv_communication_interface_internal.h"
|
|
#include "bdv_controller.h"
|
|
#include "bdv_helper.h"
|
|
#include "bdv_plugin_provider.h"
|
|
#include "logger.h"
|
|
|
|
#include <InfrastructureManagement/ism_dtc.h>
|
|
#include <LtsSession/lts_session_data.h>
|
|
#include <LtsUtils/LtsUtilsCore/outdated_position.h>
|
|
#include <LtsUtilsSettings/settings.h>
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <InfrastructureManagement/Shared/ism_mounting_point.h>
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QDir>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QJsonArray>
|
|
#include <QtCore/QJsonDocument>
|
|
#include <QtCore/QJsonObject>
|
|
#include <QJsonParseError>
|
|
#include <QtGui/QPixmap>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QXmlStreamWriter>
|
|
#include <ngltools/ngltools_filesystem.h>
|
|
#include <utility>
|
|
|
|
namespace BuildingViewer
|
|
{
|
|
static inline QString gStrSymbolFolderPath()
|
|
{
|
|
return QDir::tempPath() + QStringLiteral("/excon_bgviewer_symbols/");
|
|
}
|
|
|
|
BuildingViewerDdsController::BuildingViewerDdsController(CommunicationInterfaceInternal* connection,
|
|
VCtrl::ViewerInterface* viewerInterface)
|
|
: m_connection(connection), m_viewerInterface(viewerInterface), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger())
|
|
{
|
|
}
|
|
|
|
BuildingViewerDdsController::~BuildingViewerDdsController()
|
|
{
|
|
stopAndClearTimer();
|
|
QDir dir(gStrSymbolFolderPath());
|
|
dir.removeRecursively();
|
|
QFile::remove(getBuildingConfigurationXml());
|
|
}
|
|
|
|
bool BuildingViewerDdsController::init(QWidget* irreplacableWidget, const QString& modelPath, PluginProvider* session)
|
|
{
|
|
DEBUG_LOG(m_logger) << "BDV:: init";
|
|
m_session = session;
|
|
m_irreplacableWidget = irreplacableWidget;
|
|
|
|
m_buildingHeight.clear();
|
|
|
|
if (!m_session)
|
|
{
|
|
ERROR_LOG(m_logger) << "No m_session for BuildingViewerController!";
|
|
return false;
|
|
}
|
|
|
|
connect(m_session, &PluginProvider::destroyed, this, [this]() {
|
|
if (static_cast<QObject*>(m_session) == sender())
|
|
{
|
|
|
|
m_session = nullptr;
|
|
DEBUG_LOG(m_logger) << getSessionInfo()
|
|
<< "BDV:: BuildingViewerDdsController: m_session has been destroyed!";
|
|
stopAndClearTimer();
|
|
uninitialize();
|
|
}
|
|
});
|
|
|
|
m_asbIfc = session->asbIfc();
|
|
if (!m_asbIfc)
|
|
{
|
|
ERROR_LOG(m_logger) << "Fetching 'm_asbIfc' failed!";
|
|
return false;
|
|
}
|
|
|
|
m_bldDtc = session->bldDtc();
|
|
if (!m_bldDtc)
|
|
{
|
|
ERROR_LOG(m_logger) << "Fetching 'm_bldDtc' failed!";
|
|
return false;
|
|
}
|
|
|
|
m_ismDtc = session->ismDtc();
|
|
if (!m_ismDtc)
|
|
{
|
|
ERROR_LOG(m_logger) << "Fetching 'm_ismDtc' failed!";
|
|
return false;
|
|
}
|
|
|
|
m_pdmDtc = session->pdmDtc();
|
|
if (!m_pdmDtc)
|
|
{
|
|
ERROR_LOG(m_logger) << "Fetching 'm_pdmDtc' failed!";
|
|
return false;
|
|
}
|
|
|
|
m_ismIfc = session->ismDataIfc();
|
|
if (!m_ismIfc)
|
|
{
|
|
WARNING_LOG(m_logger) << "Fetching 'm_ismIfc' failed!";
|
|
return false;
|
|
}
|
|
connect(m_ismIfc.get(), &InfrastructureManagement::ForeignDataInterface::mountingPointChanged, this,
|
|
&BuildingViewerDdsController::onMountingPointChanged);
|
|
|
|
m_avmIfc = session->avmDataIfc();
|
|
m_blgGuiIfc = session->bldInfoIfc();
|
|
m_ppmGuiIfc = session->ppmParticipantInfoIfc();
|
|
m_aesDtc = session->aesDtc();
|
|
m_ppmDtc = session->ppmDtc();
|
|
m_srpDtc = session->srpDtc();
|
|
|
|
m_exerciseId = m_session->exerciseId();
|
|
|
|
QDir dir(gStrSymbolFolderPath());
|
|
dir.removeRecursively();
|
|
|
|
if (!dir.mkpath(QStringLiteral(".")))
|
|
{
|
|
ERROR_LOG(m_logger) << getSessionInfo() << "Could not create path \"" << gStrSymbolFolderPath()
|
|
<< "\" which is required to store BgViewer symbol files!";
|
|
return false;
|
|
}
|
|
|
|
connect(m_connection, &CommunicationInterfaceInternal::signalPlayerPicked, this,
|
|
&BuildingViewerDdsController::onPlayerPicked, Qt::UniqueConnection);
|
|
connect(m_connection, &CommunicationInterfaceInternal::signalAssetPicked, this,
|
|
&BuildingViewerDdsController::onAssetPicked, Qt::UniqueConnection);
|
|
connect(m_connection, &CommunicationInterfaceInternal::signalBgViewerInitFinished, this,
|
|
&BuildingViewerDdsController::onBgViewerInitFinished, Qt::UniqueConnection);
|
|
|
|
INFO_LOG(m_logger)
|
|
<< getSessionInfo()
|
|
<< "BuildingViewerDdsController::init: The order of the following output must be [1],[2],[3],[4],[5],[6]";
|
|
|
|
setCurrentTrainingAreaMap(modelPath);
|
|
|
|
if (m_session->isReplayPartition())
|
|
{
|
|
if (m_reCheckTimer)
|
|
{
|
|
m_reCheckTimer->stop();
|
|
delete m_reCheckTimer;
|
|
m_reCheckTimer = nullptr;
|
|
}
|
|
m_reCheckTimer = new QTimer(this);
|
|
connect(m_reCheckTimer, &QTimer::timeout, this, &BuildingViewerDdsController::reCheckHitLine);
|
|
m_reCheckTimer->start(5000);
|
|
}
|
|
else
|
|
{
|
|
m_connection->setSimuationPace(1.0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BuildingViewerDdsController::onBgViewerInitFinished()
|
|
{
|
|
DEBUG_LOG(m_logger) << "onBgViewerInitFinished";
|
|
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[2] Training area initialization acknowledged by BgViewer";
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[3] Sending mounting points, teams, ..., to BgViewer";
|
|
|
|
if (!m_pSimTimeSyncTimer)
|
|
{
|
|
m_pSimTimeSyncTimer = new QTimer(this);
|
|
m_pSimTimeSyncTimer->setInterval(m_bgviewer_simSyncTimeInterval_ms);
|
|
connect(m_pSimTimeSyncTimer, &QTimer::timeout, this, &BuildingViewerDdsController::onSynchronizeSimulationTime);
|
|
}
|
|
m_pSimTimeSyncTimer->start();
|
|
onSynchronizeSimulationTime();
|
|
|
|
syncTeams();
|
|
setPaused(false);
|
|
|
|
sendAssets();
|
|
|
|
LtsUtils::ProjectSettings* ps = LtsUtils::ProjectSettings::getInstance();
|
|
const bool useExactPositioning = ps->value(QStringLiteral("bld"), QStringLiteral("useExactPositioning")).toBool();
|
|
m_connection->excUseExactPositioning(useExactPositioning);
|
|
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[4] Getting building DDS data ('mainStructureElements')";
|
|
connect(m_ismDtc.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged, this,
|
|
&BuildingViewerDdsController::onMainStructureElementChanged);
|
|
|
|
m_ismDtc->mainStructureElements([this](const InfrastructureManagement::MainStructureElement& data) {
|
|
onMainStructureElementChanged(data, DataModification::eAdded);
|
|
});
|
|
INFO_LOG(m_logger) << getSessionInfo() << "\t[4.1] Done";
|
|
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[5] Creating participants";
|
|
|
|
createParticipants();
|
|
INFO_LOG(m_logger) << getSessionInfo() << "\t[5.1] Done";
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[5] Creating Booby Trap";
|
|
createBoobyTraps();
|
|
INFO_LOG(m_logger) << getSessionInfo() << "\t[5.2] Done";
|
|
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[6] Getting building DDS data ('buildingStates')";
|
|
connect(m_bldDtc.get(), &Buildings::Interface::Dtc::buildingStateChanged, this,
|
|
&BuildingViewerDdsController::onBuildingStateChanged, Qt::UniqueConnection);
|
|
m_bldDtc->buildingStates(
|
|
[this](const Buildings::BuildingState& data) { onBuildingStateChanged(data, DataModification::eAdded); });
|
|
connect(m_bldDtc.get(), &Buildings::Interface::Dtc::buildingHitLineInfoChanged, this,
|
|
&BuildingViewerDdsController::onBuildingHitLineInfoChanged, Qt::UniqueConnection);
|
|
INFO_LOG(m_logger) << getSessionInfo() << "\t[6.1] Done";
|
|
|
|
m_connection->excSetGlobalTransparency(
|
|
LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("GlobalTransparency"), 75)
|
|
.toInt());
|
|
m_connection->excSetGlobalViewMode(LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("UseIsoMode"), false)
|
|
.toBool());
|
|
m_connection->excSetGlobalDotsMode(LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("UseDotsMode"), false)
|
|
.toBool());
|
|
m_hitlineDuration_ms = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("maxHitlineDurationTime_s"), 10)
|
|
.toInt() *
|
|
1000;
|
|
if (m_hitlineDuration_ms < 0)
|
|
m_hitlineDuration_ms = 0;
|
|
m_connection->excSetHitlineDuration(m_hitlineDuration_ms / 1000);
|
|
|
|
if (!m_pFireEventFlashTimer)
|
|
{
|
|
m_pFireEventFlashTimer = new QTimer(this);
|
|
m_pFireEventFlashTimer->setInterval(1000);
|
|
connect(m_pFireEventFlashTimer, &QTimer::timeout, this, &BuildingViewerDdsController::onFireEventFlashTimer);
|
|
}
|
|
m_pFireEventFlashTimer->start();
|
|
|
|
if (m_srpDtc)
|
|
{
|
|
connect(m_srpDtc.get(), &SituationReplay::Interface::Dtc::sitReplayChanged, this,
|
|
&BuildingViewerDdsController::onReplayChanged, Qt::UniqueConnection);
|
|
if (m_srpDtc && m_srpDtc->isSitReplayEnabled())
|
|
{
|
|
m_srpDtc->sitReplays(
|
|
[this](const SituationReplay::SitReplay& srp) { onReplayChanged(srp, DataModification::eAdded); });
|
|
}
|
|
}
|
|
|
|
if (!m_pBuildingHitlineCleanupTimer)
|
|
{
|
|
m_pBuildingHitlineCleanupTimer = new QTimer(this);
|
|
m_pBuildingHitlineCleanupTimer->setInterval(1000 * 60 * 5);
|
|
connect(m_pBuildingHitlineCleanupTimer, &QTimer::timeout, this,
|
|
&BuildingViewerDdsController::onCleanupBuildingHitlines);
|
|
}
|
|
m_pBuildingHitlineCleanupTimer->start();
|
|
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[7] Emitting 'signalDdsInitFinished'";
|
|
m_ddsInitFinished = true;
|
|
emit signalDdsInitFinished();
|
|
onSynchronizeSimulationTime();
|
|
}
|
|
|
|
void BuildingViewerDdsController::stopAndClearTimer()
|
|
{
|
|
DEBUG_LOG(m_logger) << "stopAndClearTimer";
|
|
|
|
if (m_pFireEventFlashTimer)
|
|
m_pFireEventFlashTimer->stop();
|
|
|
|
if (m_pBuildingHitlineCleanupTimer)
|
|
m_pBuildingHitlineCleanupTimer->stop();
|
|
|
|
if (m_pSimTimeSyncTimer)
|
|
m_pSimTimeSyncTimer->stop();
|
|
}
|
|
|
|
void BuildingViewerDdsController::uninitialize()
|
|
{
|
|
DEBUG_LOG(m_logger) << "uninitialize";
|
|
if (!m_bldDtc)
|
|
return;
|
|
|
|
if (m_ddsInitFinished)
|
|
DEBUG_LOG(m_logger)
|
|
<< "Flag 'm_ddsInitFinished' set which means that initialization had been finished completely";
|
|
else
|
|
WARNING_LOG(m_logger) << "Flag 'm_ddsInitFinished' has not been set within timeout value, forcing quit!";
|
|
|
|
stopAndClearTimer();
|
|
|
|
m_connection->disconnect(this);
|
|
m_connection->systemDisconnect();
|
|
|
|
m_aesDtc->disconnect(this);
|
|
m_ppmDtc->disconnect(this);
|
|
m_ismDtc->disconnect(this);
|
|
m_ismIfc->disconnect(this);
|
|
m_bldDtc->disconnect(this);
|
|
|
|
m_avmIfc = nullptr;
|
|
m_asbIfc = nullptr;
|
|
m_aesDtc = nullptr;
|
|
m_ppmDtc = nullptr;
|
|
m_ppmGuiIfc = nullptr;
|
|
m_ismDtc = nullptr;
|
|
m_ismIfc = nullptr;
|
|
m_bldDtc = nullptr;
|
|
m_blgGuiIfc = nullptr;
|
|
|
|
m_ddsInitFinished = false;
|
|
m_buildingHeight.clear();
|
|
|
|
DEBUG_LOG(m_logger) << "BuildingViewerDdsController::uninitialize() finished";
|
|
}
|
|
|
|
QPointF BuildingViewerDdsController::toRelativePosition(const Core::GeoPosition& geoPos) const
|
|
{
|
|
if (std::isnan(geoPos.lon) || std::isnan(geoPos.lat) || qIsNull(geoPos.lon) || qIsNull(geoPos.lat))
|
|
return {};
|
|
|
|
LtsCoordTrans::UtmCoordinate coord(
|
|
LtsCoordTrans::UtmCoordinate::fromGeographic(geoPos.lon, geoPos.lat, m_baseCoordinate.zone));
|
|
return coord.relativeTo(m_baseCoordinate);
|
|
}
|
|
|
|
LtsUtils::QPoint3D BuildingViewerDdsController::toRelative3D(const Core::GeoPosition& geoPos) const
|
|
{
|
|
const auto ret = toRelativePosition(geoPos);
|
|
if (ret.isNull())
|
|
return {};
|
|
return LtsUtils::QPoint3D(ret.x(), ret.y(), geoPos.z);
|
|
}
|
|
|
|
bool BuildingViewerDdsController::clear()
|
|
{
|
|
DEBUG_LOG(m_logger) << "clear m_connection->playerClear();";
|
|
m_connection->playerClear();
|
|
return true;
|
|
}
|
|
|
|
bool BuildingViewerDdsController::syncTeams()
|
|
{
|
|
if (!m_ppmDtc)
|
|
return false;
|
|
INFO_LOG(m_logger) << "BuildingViewerDdsController::syncTeams";
|
|
|
|
m_connection->setTeamConfiguration(m_ppmDtc->partys());
|
|
return true;
|
|
}
|
|
|
|
bool BuildingViewerDdsController::setPaused(bool bPaused)
|
|
{
|
|
m_connection->systemSetPaused(bPaused);
|
|
return true;
|
|
}
|
|
|
|
bool BuildingViewerDdsController::calcBaseCoordinate(
|
|
const QMap<qint32, InfrastructureManagement::MainStructureElement>& mseMap)
|
|
{
|
|
bool bAtLeasOneFound = false;
|
|
Core::GeoPosition geoPosition;
|
|
for (const auto& mse : mseMap)
|
|
{
|
|
if (!bAtLeasOneFound)
|
|
{
|
|
bAtLeasOneFound = true;
|
|
geoPosition = mse.geoPosition;
|
|
}
|
|
else
|
|
{
|
|
geoPosition.lat = std::min(geoPosition.lat, mse.geoPosition.lat);
|
|
geoPosition.lon = std::min(geoPosition.lon, mse.geoPosition.lon);
|
|
}
|
|
}
|
|
if (!bAtLeasOneFound)
|
|
{
|
|
WARNING_LOG(m_logger) << "No building available in exercise " << m_exerciseId;
|
|
return false;
|
|
}
|
|
m_baseCoordinate = LtsCoordTrans::UtmCoordinate::fromGeographic(geoPosition.lon, geoPosition.lat);
|
|
return true;
|
|
}
|
|
|
|
bool BuildingViewerDdsController::isOutdatedPosition(qint64 sampleTime) const
|
|
{
|
|
if (m_session == nullptr)
|
|
return true;
|
|
return LtsUtils::isPositionTimeOutdated(m_session->partitionTime(), sampleTime);
|
|
}
|
|
|
|
bool BuildingViewerDdsController::writeBuildingDataToXML(const QString& modelPath)
|
|
{
|
|
QFile f(getBuildingConfigurationXml());
|
|
if (!f.open(QIODevice::WriteOnly))
|
|
{
|
|
WARNING_LOG(m_logger) << "Can not open file " << f.fileName() << " for writing: " << f.errorString();
|
|
return false;
|
|
}
|
|
m_knownBuildings.clear();
|
|
|
|
QMap<qint32, InfrastructureManagement::MainStructureElement> mseMap;
|
|
|
|
QFile jsonConfig(modelPath + QStringLiteral("/buildings.json"));
|
|
if (!jsonConfig.exists())
|
|
jsonConfig.setFileName(modelPath + QStringLiteral("/../buildings.json"));
|
|
|
|
DEBUG_LOG(m_logger) << "jsonConfig:" << jsonConfig.fileName();
|
|
if (jsonConfig.exists() && jsonConfig.open(QIODevice::ReadOnly))
|
|
{
|
|
m_ismDtc->disconnect(this);
|
|
InfrastructureManagement::MainStructureElement mse;
|
|
const QJsonArray arr = QJsonDocument::fromJson(jsonConfig.readAll()).array();
|
|
for (const auto& ele : arr)
|
|
{
|
|
const auto bi = BuildingInfo::fromJson(ele.toObject());
|
|
if (!bi.isValid())
|
|
continue;
|
|
mse.id = bi.id;
|
|
mse.angle = bi.angleDegree;
|
|
mse.geoPosition = Core::GeoPosition(bi.position.x(), bi.position.y(), bi.position.z());
|
|
mse.name = bi.name.toStdString();
|
|
mseMap.insert(mse.id, mse);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_ismDtc->waitForHistoricalDataMainStructureElement(m_dtc_timeout_ms);
|
|
const auto mMap = m_ismDtc->mainStructureElements();
|
|
for (const auto& mse : mMap)
|
|
{
|
|
if (mse.modelId == 0)
|
|
continue;
|
|
if (!m_ismDtc->isElementAssignedToExercise(mse, m_exerciseId))
|
|
{
|
|
DEBUG_LOG(m_logger)
|
|
<< "Not writing mse with id " << mse.id
|
|
<< " to building xml file since it is not assigned to the current exercise which has id "
|
|
<< m_exerciseId;
|
|
continue;
|
|
}
|
|
mseMap.insert(mse.id, mse);
|
|
}
|
|
}
|
|
INFO_LOG(m_logger) << getSessionInfo()
|
|
<< "writeBuildingDataToXML(): Identified Main Structure Elements: " << mseMap.size();
|
|
|
|
if (!calcBaseCoordinate(mseMap))
|
|
{
|
|
WARNING_LOG(m_logger) << "failed to calc base coordinate";
|
|
return false;
|
|
}
|
|
|
|
QXmlStreamWriter sw(&f);
|
|
sw.setAutoFormatting(true);
|
|
sw.setAutoFormattingIndent(2);
|
|
// Qt6: QXmlStreamWriter::setCodec removed; it writes UTF-8 by default.
|
|
sw.writeStartDocument(QStringLiteral("1.0"), true);
|
|
sw.writeStartElement(QStringLiteral("MetaData"));
|
|
{
|
|
sw.writeStartElement(QStringLiteral("Origin"));
|
|
{
|
|
|
|
const auto geo = m_baseCoordinate.geographic();
|
|
sw.writeAttribute(QStringLiteral("Longitude"), QString::number(geo.x() * 180 / M_PI, 'f', 13));
|
|
sw.writeAttribute(QStringLiteral("Latitude"), QString::number(geo.y() * 180 / M_PI, 'f', 13));
|
|
}
|
|
sw.writeEndElement();
|
|
|
|
sw.writeStartElement(QStringLiteral("Tag"));
|
|
sw.writeStartElement(QStringLiteral("Group"));
|
|
|
|
for (const InfrastructureManagement::MainStructureElement& mse : std::as_const(mseMap))
|
|
{
|
|
m_knownBuildings += mse.id;
|
|
|
|
sw.writeStartElement(QStringLiteral("Model"));
|
|
sw.writeAttribute(QStringLiteral("Type"), QStringLiteral("House"));
|
|
{
|
|
const QString name(QString::fromStdString(mse.name));
|
|
sw.writeStartElement(QStringLiteral("Description"));
|
|
sw.writeAttribute(QStringLiteral("Name"), name);
|
|
sw.writeEndElement();
|
|
sw.writeStartElement(QStringLiteral("Feature"));
|
|
sw.writeAttribute(QStringLiteral("Id"), BuildingViewer::databaseId2FeatureId(mse.id));
|
|
sw.writeEndElement();
|
|
sw.writeStartElement(QStringLiteral("Orientation"));
|
|
|
|
sw.writeAttribute(QStringLiteral("Angle"), QString::number(360 - mse.angle));
|
|
sw.writeEndElement();
|
|
|
|
const QPointF offset(toRelativePosition(mse.geoPosition));
|
|
sw.writeStartElement(QStringLiteral("Position"));
|
|
sw.writeAttribute(QStringLiteral("X"), QString::number(offset.x(), 'f', 13));
|
|
sw.writeAttribute(QStringLiteral("Y"), QString::number(offset.y(), 'f', 13));
|
|
sw.writeAttribute(QStringLiteral("Z"), QString::number(mse.geoPosition.z, 'f', 13));
|
|
sw.writeEndElement();
|
|
sw.writeStartElement(QStringLiteral("File"));
|
|
sw.writeAttribute(QStringLiteral("Name"), name + QStringLiteral(".flt"));
|
|
sw.writeEndElement();
|
|
sw.writeStartElement(QStringLiteral("STREIF"));
|
|
sw.writeAttribute(QStringLiteral("Name"), name + QStringLiteral(".flt.xml"));
|
|
sw.writeEndElement();
|
|
}
|
|
sw.writeEndElement();
|
|
}
|
|
sw.writeEndElement();
|
|
sw.writeEndElement();
|
|
}
|
|
sw.writeEndElement();
|
|
sw.writeEndDocument();
|
|
f.close();
|
|
|
|
std::sort(m_knownBuildings.begin(), m_knownBuildings.end());
|
|
return true;
|
|
}
|
|
|
|
QString BuildingViewerDdsController::getBuildingConfigurationXml() const
|
|
{
|
|
return QDir::tempPath() + QStringLiteral("/buildings_%1.xml").arg(m_exerciseId);
|
|
}
|
|
|
|
void BuildingViewerDdsController::clearAssets()
|
|
{
|
|
INFO_LOG(m_logger) << "clearAssets: Removing " << m_knownAssetsInBgViewer.size() << " assets";
|
|
|
|
for (const auto& strUniqueAsset : std::as_const(m_knownAssetsInBgViewer))
|
|
m_connection->extHideMountingPointAsset(strUniqueAsset);
|
|
|
|
m_mountingPointId2AssetType.clear();
|
|
m_knownAssetsInBgViewer.clear();
|
|
}
|
|
|
|
void BuildingViewerDdsController::sendAssets()
|
|
{
|
|
clearAssets();
|
|
|
|
std::function<void(const InfrastructureManagement::MountingPoint&)> cb =
|
|
[this](const InfrastructureManagement::MountingPoint& mp) {
|
|
handleMountingPoint(mp, DataModification::eAdded);
|
|
};
|
|
m_ismIfc->mountingPoints(cb);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onReplayChanged(const SituationReplay::SitReplay& srp, DataModification modType)
|
|
{
|
|
Q_UNUSED(modType)
|
|
|
|
switch (srp.state)
|
|
{
|
|
case SituationReplay::SrpState::Stopped:
|
|
m_connection->setSimuationPace(1.0);
|
|
|
|
break;
|
|
case SituationReplay::SrpState::Started:
|
|
case SituationReplay::SrpState::Resumed:
|
|
m_connection->setSimuationPace(srp.pace);
|
|
|
|
break;
|
|
case SituationReplay::SrpState::Paused:
|
|
m_connection->setSimuationPace(0.0);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void BuildingViewerDdsController::handleMountingPoint(const InfrastructureManagement::MountingPoint& mp,
|
|
DataModification modType)
|
|
{
|
|
if (!isBuildingKnown(mp.buildingId()))
|
|
return;
|
|
if (m_session->isReplayPartition())
|
|
return;
|
|
|
|
const int mountingPointId = mp.id();
|
|
|
|
auto itMp = m_knownAssetsInBgViewer.find(mountingPointId);
|
|
if (itMp != m_knownAssetsInBgViewer.end())
|
|
{
|
|
if (modType == DataModification::eRemoved || !mp.hasAssociatedAsset())
|
|
{
|
|
m_connection->extHideMountingPointAsset(itMp.value());
|
|
m_knownAssetsInBgViewer.erase(itMp);
|
|
}
|
|
|
|
m_mountingPointId2AssetType.remove(mountingPointId);
|
|
if (modType == DataModification::eRemoved)
|
|
return;
|
|
}
|
|
|
|
if (!mp.hasAssociatedAsset())
|
|
return;
|
|
|
|
BuildingViewer::AssetTypes assetType;
|
|
QString strAssetType;
|
|
switch (mp.type())
|
|
{
|
|
case InfrastructureManagement::MountingPointType::AnnouncementUnit:
|
|
assetType = BuildingViewer::AssetTypes::AnnouncementUnit;
|
|
strAssetType = QStringLiteral("AnnouncementUnit");
|
|
break;
|
|
case InfrastructureManagement::MountingPointType::EffectGenerationControlUnit:
|
|
assetType = BuildingViewer::AssetTypes::EffectGenerationUnit;
|
|
strAssetType = QStringLiteral("EffectGenerationControlUnit");
|
|
break;
|
|
case InfrastructureManagement::MountingPointType::IndoorCamera: {
|
|
bool hasSpeaker = false;
|
|
QJsonParseError jError;
|
|
const QJsonDocument doc(QJsonDocument::fromJson(mp.properties(), &jError));
|
|
if (jError.error == QJsonParseError::NoError)
|
|
{
|
|
const QJsonObject obj(doc.object());
|
|
hasSpeaker = obj[QStringLiteral("hasSpeaker")].toBool();
|
|
}
|
|
assetType = hasSpeaker ? BuildingViewer::AssetTypes::IndoorCameraWithSpeaker
|
|
: BuildingViewer::AssetTypes::IndoorCamera;
|
|
strAssetType = hasSpeaker ? QStringLiteral("IndoorCameraWithSpeaker") : QStringLiteral("IndoorCamera");
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
QVector3D position(mp.relPosition().offsetX, mp.relPosition().offsetY, mp.relPosition().offsetZ);
|
|
QString strfeatureID = BuildingViewer::databaseId2FeatureId(mp.buildingId());
|
|
QString strMountingPointID = strfeatureID + QStringLiteral("_MountingPoint_") + QString::number(mountingPointId);
|
|
|
|
DEBUG_LOG(m_logger) << "Sending asset for building id " << mp.buildingId() << " (" << strfeatureID << ")"
|
|
<< " with asset_id " << mp.associatedAssetId() << " matched to structure_element_id " << mp.id()
|
|
<< " as \"" << strMountingPointID << " \""
|
|
<< " with asset type \"" << strAssetType << "\"";
|
|
|
|
if (m_mountingPointId2AssetType.contains(mountingPointId))
|
|
WARNING_LOG(m_logger) << "Mounting point id " << mountingPointId
|
|
<< " already used for other asset?! Overriding...";
|
|
|
|
m_mountingPointId2AssetType[mountingPointId] = assetType;
|
|
m_knownAssetsInBgViewer[mountingPointId] = strMountingPointID;
|
|
|
|
m_connection->extSetMountingPointAsset(strfeatureID, strMountingPointID, position, assetType);
|
|
}
|
|
|
|
void BuildingViewerDdsController::setCurrentTrainingAreaMap(const QString& modelPath)
|
|
{
|
|
if (!m_connection)
|
|
{
|
|
WARNING_LOG(m_logger) << "BuildingViewerDdsController::setCurrentTrainingAreaMap: (!m_connection)";
|
|
return;
|
|
}
|
|
|
|
m_connection->excDeleteBuildingList();
|
|
|
|
INFO_LOG(m_logger) << getSessionInfo() << "[1] Initializing BgViewer with training area...";
|
|
|
|
if (modelPath.isEmpty())
|
|
{
|
|
QString strErr = tr("Error initializing Building Viewer - can not find map directory.");
|
|
DEBUG_LOG(m_logger) << getSessionInfo() << "setCurrentTrainingAreaMap: " << strErr;
|
|
QWidget* parentWidgetx = (m_viewerInterface ? m_viewerInterface->getMainWindow() : nullptr);
|
|
LtsWidgets::LtsMessageDialog::showMessage(parentWidgetx, strErr, tr("Error"));
|
|
return;
|
|
}
|
|
INFO_LOG(m_logger) << "Map path for building viewer: " << modelPath;
|
|
|
|
writeBuildingDataToXML(modelPath);
|
|
|
|
QString pathToXml = getBuildingConfigurationXml();
|
|
|
|
QFileInfo file(pathToXml);
|
|
if (!file.exists())
|
|
{
|
|
FATAL_LOG(m_logger) << getSessionInfo()
|
|
<< "setCurrentTrainingAreaMap: Required buildings xml file does not exist: \"" << pathToXml
|
|
<< "\", BuildingViewer initialization FAILED!";
|
|
return;
|
|
}
|
|
|
|
const QString shadersPath = NGL::directory(NGL::FileType::ConfigFile, QStringLiteral("BuildingViewer"));
|
|
|
|
QString additionalPath;
|
|
QVector<QString> directoriesToSearch = NGL::directories(NGL::FileType::ConfigFile);
|
|
#ifndef WIN32
|
|
directoriesToSearch += QStringLiteral("/opt");
|
|
#endif
|
|
|
|
for (const QString& d : std::as_const(directoriesToSearch))
|
|
{
|
|
const QDir dir(d + QStringLiteral("/BuildingModels/StaticMeshes/"));
|
|
if (dir.exists() && dir.isReadable())
|
|
additionalPath = dir.absolutePath();
|
|
INFO_LOG(getLogger("BgViewerController"))
|
|
<< "Map path '" << dir.absolutePath() << "' either does not exist or is not readable - skipping";
|
|
}
|
|
|
|
std::vector<std::string> paths;
|
|
paths.push_back(modelPath.toStdString());
|
|
paths.push_back(shadersPath.toStdString());
|
|
if (!additionalPath.isEmpty())
|
|
{
|
|
paths.push_back(additionalPath.toStdString());
|
|
}
|
|
m_connection->excConnect(paths, pathToXml);
|
|
}
|
|
|
|
bool BuildingViewerDdsController::isValidParticipant(const ParticipantManagement::Participant& participant) const
|
|
{
|
|
if (participant.id == 0)
|
|
{
|
|
if (m_logger.isDebugEnabled())
|
|
DEBUG_LOG(m_logger) << getSessionInfo()
|
|
<< "isValidParticipant: Not handling invalid participant with invalid id (0)";
|
|
return false;
|
|
}
|
|
|
|
if (participant.isDeleted)
|
|
{
|
|
DEBUG_LOG(m_logger) << getSessionInfo() << "isValidParticipant: Not handling participant with id "
|
|
<< participant.id << " since it is marked as 'deleted'";
|
|
return false;
|
|
}
|
|
|
|
if (m_session == nullptr)
|
|
return false;
|
|
|
|
if (!m_session->isVisibleParty(participant.partyId))
|
|
{
|
|
DEBUG_LOG(m_logger) << getSessionInfo() << "isValidParticipant: Not handling participant with id "
|
|
<< participant.id << " since it is not visible for party with id "
|
|
<< participant.partyId;
|
|
return false;
|
|
}
|
|
|
|
if (!m_ppmDtc)
|
|
return false;
|
|
|
|
ParticipantManagement::TypeClass tc = m_ppmDtc->type(participant.typeId).typeClass;
|
|
if (tc == ParticipantManagement::TypeClass::SupportUnit || tc == ParticipantManagement::TypeClass::Unit)
|
|
{
|
|
DEBUG_LOG(m_logger) << getSessionInfo() << "isValidParticipant: Not handling participant with id "
|
|
<< participant.id << " since its type is 'SupportUnit', 'Unit' or 'Structure'";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BuildingViewerDdsController::createBoobyTraps()
|
|
{
|
|
if (!m_aesDtc)
|
|
return;
|
|
DEBUG_LOG(m_logger) << "BDV:: create Booby Traps: " << m_session->exerciseId()
|
|
<< " is replay: " << m_session->isReplayPartition();
|
|
|
|
m_aesDtc->enableBoobyTrap();
|
|
|
|
const int sizeL = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("PlayerSymbolSize"), 20)
|
|
.toInt();
|
|
|
|
auto cb = [this, sizeL](const AreaEffectSimulation::BoobyTrap& trap) { createBoobyTrap(trap, sizeL); };
|
|
m_aesDtc->boobyTraps(cb);
|
|
|
|
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged, this,
|
|
&BuildingViewerDdsController::onBoobyTrapChanged, Qt::UniqueConnection);
|
|
}
|
|
|
|
void BuildingViewerDdsController::createParticipants()
|
|
{
|
|
if (!m_ppmDtc)
|
|
return;
|
|
DEBUG_LOG(m_logger) << "BDV:: create participants: " << m_session->exerciseId()
|
|
<< " is replay: " << m_session->isReplayPartition();
|
|
|
|
const int sizeL = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("PlayerSymbolSize"), 20)
|
|
.toInt();
|
|
|
|
const QMap<int, ParticipantManagement::ParticipantPosition> positionMap = m_ppmDtc->participantPositions();
|
|
auto cb = [this, &positionMap, sizeL](const ParticipantManagement::Participant& part) {
|
|
if (!isValidParticipant(part))
|
|
return;
|
|
createParticipant(part, positionMap.value(part.id), sizeL);
|
|
};
|
|
m_ppmDtc->participants(cb);
|
|
|
|
m_ppmDtc->enableParticipant();
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged, this,
|
|
&BuildingViewerDdsController::onParticipantChanged, Qt::UniqueConnection);
|
|
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::hitLineInfoChanged, this,
|
|
&BuildingViewerDdsController::onHitLineInfoChanged, Qt::UniqueConnection);
|
|
m_ppmDtc->enableHitLineInfo();
|
|
|
|
m_ppmDtc->enableParty();
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::partyChanged, this,
|
|
&BuildingViewerDdsController::onPartyChanged, Qt::UniqueConnection);
|
|
|
|
m_ppmDtc->enableParticipantPosition();
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged, this,
|
|
&BuildingViewerDdsController::onPositionChanged, Qt::UniqueConnection);
|
|
|
|
m_ppmDtc->enableParticipantGearEvent();
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantGearEventChanged, this,
|
|
&BuildingViewerDdsController::onParticipantGearEventChanged, Qt::UniqueConnection);
|
|
|
|
m_ppmDtc->enableDynamicParticipantData();
|
|
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged, this,
|
|
&BuildingViewerDdsController::onDynParticipantChanged, Qt::UniqueConnection);
|
|
|
|
m_ppmDtc->participantGearEvents([this](const ParticipantManagement::ParticipantGearEvent& data) {
|
|
onParticipantGearEventChanged(data, DataModification::eAdded);
|
|
});
|
|
}
|
|
|
|
void BuildingViewerDdsController::onBoobyTrapChanged(const AreaEffectSimulation::BoobyTrap& boobyTrap,
|
|
DataModification modType)
|
|
{
|
|
switch (modType)
|
|
{
|
|
case DataModification::eModified:
|
|
deleteParticipant(990000 + boobyTrap.id);
|
|
Q_FALLTHROUGH();
|
|
case DataModification::eAdded: {
|
|
int sizeL = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("PlayerSymbolSize"), 20)
|
|
.toInt();
|
|
createBoobyTrap(boobyTrap, sizeL);
|
|
break;
|
|
}
|
|
case DataModification::eRemoved: {
|
|
deleteParticipant(990000 + boobyTrap.id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildingViewerDdsController::onParticipantChanged(const ParticipantManagement::Participant& participant,
|
|
DataModification modType)
|
|
{
|
|
if (!isValidParticipant(participant))
|
|
return;
|
|
|
|
switch (modType)
|
|
{
|
|
case DataModification::eModified:
|
|
deleteParticipant(participant.id);
|
|
Q_FALLTHROUGH();
|
|
case DataModification::eAdded: {
|
|
int sizeL = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("PlayerSymbolSize"), 20)
|
|
.toInt();
|
|
createParticipant(participant, m_ppmDtc->participantPosition(participant.id), sizeL);
|
|
break;
|
|
}
|
|
case DataModification::eRemoved: {
|
|
deleteParticipant(participant.id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildingViewerDdsController::onDynParticipantChanged(const ParticipantManagement::DynamicParticipantData& data,
|
|
DataModification modType)
|
|
{
|
|
int sizeL = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("PlayerSymbolSize"), 20)
|
|
.toInt();
|
|
switch (modType)
|
|
{
|
|
case DataModification::eAdded:
|
|
case DataModification::eModified:
|
|
if (m_ppmDtc)
|
|
createParticipant(m_ppmDtc->participant(data.participantId),
|
|
m_ppmDtc->participantPosition(data.participantId), sizeL);
|
|
break;
|
|
case DataModification::eRemoved: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildingViewerDdsController::onHitLineInfoChanged(const ParticipantManagement::HitLineInfo& hli,
|
|
DataModification modType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "X 0000 onHitLineInfoChanged id=" << hli.id;
|
|
|
|
if (modType == DataModification::eRemoved)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onHitLineInfoChanged removeHit id=" << hli.id;
|
|
m_connection->removeRoomHit(hli.id);
|
|
return;
|
|
}
|
|
|
|
if (!m_ppmDtc)
|
|
return;
|
|
m_ppmDtc->waitForHistoricalDataParticipant();
|
|
|
|
const ParticipantManagement::Participant& partSrc = m_ppmDtc->participant(hli.originatorParticipantId);
|
|
const ParticipantManagement::Participant& partDst = m_ppmDtc->participant(hli.injuryParticipantId);
|
|
if (partSrc.id == 0 || partDst.id == 0)
|
|
{
|
|
ERROR_LOG(m_logger) << "no knownn part: " << partSrc.id << " dst: " << partDst.id;
|
|
return;
|
|
}
|
|
|
|
const QString& strFeatureID = BuildingViewer::databaseId2FeatureId(hli.injuryBuildingId);
|
|
if (!m_viewerInterface->isViewOpen(strFeatureID.toStdString()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "building not opened: hli.buildingId()=" << hli;
|
|
return;
|
|
}
|
|
|
|
if (hli.injuryBuildingId != 0)
|
|
{
|
|
auto itBuildingHit = m_buildingHits.constFind(hli.injuryBuildingId);
|
|
if (itBuildingHit != m_buildingHits.constEnd())
|
|
{
|
|
const QVector<BuildingHit>& bHits = itBuildingHit.value();
|
|
|
|
DEBUG_LOG(m_logger) << "Got Hit lines to add? " << bHits.size();
|
|
for (const BuildingHit& bh : bHits)
|
|
{
|
|
|
|
const bool testFlag = (bh.chargeTypeId == hli.chargeTypeId &&
|
|
bh.originatorParticipantId == hli.originatorParticipantId &&
|
|
hli.sampleTime - m_hitlineDuration_ms < bh.sampleTime);
|
|
DEBUG_LOG(m_logger) << "testFlag " << testFlag;
|
|
|
|
if (bh.chargeTypeId == hli.chargeTypeId &&
|
|
bh.originatorParticipantId == hli.originatorParticipantId &&
|
|
hli.sampleTime - m_hitlineDuration_ms < bh.sampleTime)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "onHitLineInfoChanged: " << hli;
|
|
|
|
int sizeL = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("PlayerSymbolSize"), 20)
|
|
.toInt();
|
|
DEBUG_LOG(m_logger) << "onHitLineInfoChanged createPlayerSymbols";
|
|
createPlayerSymbols(partDst, sizeL);
|
|
|
|
LtsUtils::QPoint3D originatorSrc = toRelative3D(hli.originatorPos);
|
|
LtsUtils::QPoint3D injuryDest = toRelative3D(hli.injuryPos);
|
|
|
|
DEBUG_LOG(m_logger) << "onHitLineInfoChanged playerHit"
|
|
<< " hli.originatorPos() z=" << hli.originatorPos.z
|
|
<< " hli.injuryPos() z=" << hli.injuryPos.z << " Src=" << originatorSrc.x() << "/"
|
|
<< originatorSrc.y() << "/" << originatorSrc.z() << " Dest= " << injuryDest.x() << "/"
|
|
<< injuryDest.y() << "/" << injuryDest.z()
|
|
<< " hli.originatorBuildingId=" << hli.originatorBuildingId
|
|
<< " hli.originatorRoomId=" << hli.originatorRoomId
|
|
<< " hli.injuryBuildingId=" << hli.injuryBuildingId
|
|
<< " hli.injuryRoomId=" << hli.injuryRoomId;
|
|
|
|
DEBUG_LOG(m_logger) << "Before z-correction:"
|
|
<< " Src=" << originatorSrc.x() << "/" << originatorSrc.y() << "/" << originatorSrc.z()
|
|
<< " Dest= " << injuryDest.x() << "/" << injuryDest.y() << "/" << injuryDest.z();
|
|
|
|
double zAddOriginator = m_buildingHeight.value(hli.originatorBuildingId, 0);
|
|
double zAddInjury = m_buildingHeight.value(hli.injuryBuildingId, 0);
|
|
DEBUG_LOG(m_logger) << "z-correction: zAddOriginator=" << zAddOriginator;
|
|
DEBUG_LOG(m_logger) << "z-correction: zAddInjury=" << zAddInjury;
|
|
|
|
if (hli.originatorBuildingId <= 0)
|
|
zAddOriginator = zAddInjury;
|
|
|
|
if (hli.injuryBuildingId <= 0)
|
|
zAddInjury = zAddOriginator;
|
|
|
|
originatorSrc.setZ(originatorSrc.z() + zAddInjury);
|
|
injuryDest.setZ(injuryDest.z() + zAddInjury);
|
|
|
|
DEBUG_LOG(m_logger) << "After z-correction:"
|
|
<< " Src=" << originatorSrc.x() << "/" << originatorSrc.y() << "/" << originatorSrc.z()
|
|
<< " Dest= " << injuryDest.x() << "/" << injuryDest.y() << "/" << injuryDest.z();
|
|
|
|
int buildingIdSrc = -1;
|
|
int roomNumberSrc = -1;
|
|
{
|
|
auto itId2Number = m_roomId2RoomNumber.constFind(hli.originatorBuildingId);
|
|
if (itId2Number != m_roomId2RoomNumber.constEnd())
|
|
{
|
|
const auto& mapRooms = itId2Number.value();
|
|
const auto itRoom = mapRooms.constFind(hli.originatorRoomId);
|
|
if (itRoom != mapRooms.constEnd())
|
|
{
|
|
buildingIdSrc = hli.originatorBuildingId;
|
|
roomNumberSrc = itRoom.value();
|
|
}
|
|
else
|
|
WARNING_LOG(m_logger) << "onHitLineInfoChanged: Room id can not be mapped! Building id: "
|
|
<< hli.injuryBuildingId << ", originatorRoomId: " << hli.originatorRoomId;
|
|
}
|
|
}
|
|
|
|
int buildingIdDst = -1;
|
|
int roomNumberDst = -1;
|
|
{
|
|
auto itId2Number = m_roomId2RoomNumber.constFind(hli.injuryBuildingId);
|
|
if (itId2Number != m_roomId2RoomNumber.constEnd())
|
|
{
|
|
const auto& mapRooms = itId2Number.value();
|
|
const auto itRoom = mapRooms.constFind(hli.injuryRoomId);
|
|
if (itRoom != mapRooms.constEnd())
|
|
{
|
|
buildingIdDst = hli.injuryBuildingId;
|
|
roomNumberDst = itRoom.value();
|
|
}
|
|
else
|
|
WARNING_LOG(m_logger) << "onHitLineInfoChanged: Room id can not be mapped! Building id: "
|
|
<< hli.injuryBuildingId << ", room id: " << hli.injuryRoomId;
|
|
}
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "After translate buildingId, roomId:"
|
|
<< " hli.originatorBuildingId=" << hli.originatorBuildingId << " -> " << buildingIdSrc
|
|
<< " hli.originatorRoomId=" << hli.originatorRoomId << " -> " << roomNumberSrc
|
|
<< " hli.injuryBuildingId=" << hli.injuryBuildingId << " -> " << buildingIdDst
|
|
<< " hli.injuryRoomId=" << hli.injuryBuildingId << " -> " << roomNumberDst
|
|
<< " hli.sampleTime=" << hli.sampleTime;
|
|
int lineType = 2;
|
|
m_pdmDtc->chargeType(hli.chargeTypeId);
|
|
|
|
// DDS enums are enum class; lineType is int.
|
|
lineType = static_cast<int>(m_pdmDtc->getChargeTypeSpecialProperty(hli.chargeTypeId, 0, 0));
|
|
|
|
m_connection->playerHit(hli.id, partSrc.id, partDst.id, originatorSrc, injuryDest, buildingIdSrc,
|
|
roomNumberSrc, buildingIdDst, roomNumberDst, lineType, hli.sampleTime);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onPartyChanged(const ParticipantManagement::Party& party, DataModification modType)
|
|
{
|
|
if (modType == DataModification::eRemoved)
|
|
return;
|
|
|
|
int sizeL = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("PlayerSymbolSize"), 20)
|
|
.toInt();
|
|
updatePlayerSymbols(sizeL, party.id);
|
|
syncTeams();
|
|
}
|
|
|
|
void BuildingViewerDdsController::onPositionChanged(const ParticipantManagement::ParticipantPosition& position,
|
|
DataModification modType)
|
|
{
|
|
|
|
if (modType == DataModification::eRemoved)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "onPositionChanged: " << position;
|
|
|
|
int buildingID = -1;
|
|
int roomNumber = -1;
|
|
auto itId2Number = m_roomId2RoomNumber.constFind(position.buildingId);
|
|
if (itId2Number != m_roomId2RoomNumber.constEnd())
|
|
{
|
|
const auto& mapRooms = itId2Number.value();
|
|
const auto itRoom = mapRooms.constFind(position.roomId);
|
|
if (itRoom != mapRooms.constEnd())
|
|
{
|
|
buildingID = position.buildingId;
|
|
roomNumber = itRoom.value();
|
|
}
|
|
else
|
|
{
|
|
WARNING_LOG(m_logger) << "onPositionChanged: Room id can not be mapped! Building id: "
|
|
<< position.buildingId << ", room id: " << position.roomId;
|
|
}
|
|
}
|
|
|
|
if ((roomNumber < 0) && (buildingID <= 0) && (position.buildingId > 0) &&
|
|
(position.reportingSource == Core::PositionReportingSource::Ops ||
|
|
position.reportingSource == Core::PositionReportingSource::Wls))
|
|
{
|
|
buildingID = position.buildingId;
|
|
}
|
|
|
|
const int playerId = position.participantId;
|
|
auto it = m_lastPositionTime.find(playerId);
|
|
if (it == m_lastPositionTime.end())
|
|
{
|
|
m_lastPositionTime.insert(playerId, position.sampleTime);
|
|
if (isOutdatedPosition(position))
|
|
m_connection->setPlayerVisible(playerId, false);
|
|
}
|
|
else
|
|
{
|
|
if (isOutdatedPosition(it.value()) && !isOutdatedPosition(position))
|
|
m_connection->setPlayerVisible(playerId, true);
|
|
it.value() = position.sampleTime;
|
|
}
|
|
|
|
if (buildingID <= 0 && roomNumber <= 0)
|
|
{
|
|
m_connection->setPlayerVisible(playerId, false);
|
|
}
|
|
else
|
|
{
|
|
m_connection->setPlayerVisible(playerId, true);
|
|
}
|
|
|
|
if (!m_baseCoordinate.isValid())
|
|
{
|
|
WARNING_LOG(m_logger)
|
|
<< "not calling playerPlace as there is no base coordinate set - are there no buildings in this exercise?";
|
|
return;
|
|
}
|
|
|
|
m_connection->playerPlace(position.participantId, toRelative3D(position.pos), buildingID, roomNumber,
|
|
position.reportingSource);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onParticipantGearEventChanged(const ParticipantManagement::ParticipantGearEvent& data,
|
|
DataModification modType)
|
|
{
|
|
switch (modType)
|
|
{
|
|
case DataModification::eAdded:
|
|
case DataModification::eModified: {
|
|
m_participantGearEvents[data.participantId] = data.sampleTime;
|
|
break;
|
|
}
|
|
case DataModification::eRemoved: {
|
|
|
|
const int playerId = data.participantId;
|
|
m_participantGearEvents.remove(playerId);
|
|
m_connection->playerUpdatePlayerSymbol(playerId, buildPlayerSymbolFilePath(playerId, cColorSymbolId));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuildingViewerDdsController::createParticipant(const ParticipantManagement::Participant& participant,
|
|
const ParticipantManagement::ParticipantPosition& position,
|
|
int sizeL)
|
|
{
|
|
if (!m_baseCoordinate.isValid())
|
|
{
|
|
WARNING_LOG(m_logger)
|
|
<< "not calling playerPlace as there is no base coordinate set - are there no buildings in this exercise?";
|
|
return;
|
|
}
|
|
|
|
if (!isValidParticipant(participant))
|
|
return;
|
|
|
|
createPlayerSymbols(participant, sizeL);
|
|
|
|
m_connection->playerCreate(participant.id, participant.partyId, buildPlayerSymbolFilePath(participant.id),
|
|
toRelative3D(position.pos), position.buildingId,
|
|
m_roomId2RoomNumber.value(position.buildingId).value(position.roomId),
|
|
position.reportingSource);
|
|
|
|
const bool isOutdated = isOutdatedPosition(position);
|
|
const int participantId = participant.id;
|
|
const bool isValid =
|
|
(position.buildingId > 0 && m_roomId2RoomNumber.value(position.buildingId).value(position.roomId) > 0);
|
|
m_connection->setPlayerVisible(participantId, (!isOutdated && isValid));
|
|
m_lastPositionTime.insert(participantId, position.sampleTime);
|
|
}
|
|
|
|
void BuildingViewerDdsController::createBoobyTrap(const AreaEffectSimulation::BoobyTrap& trap, int sizeL)
|
|
{
|
|
if (trap.effectStateId != AreaEffectSimulation::AesEffectState::EsActivated &&
|
|
trap.effectStateId != AreaEffectSimulation::AesEffectState::EsPrepared)
|
|
return;
|
|
|
|
ParticipantManagement::Participant participant;
|
|
if (trap.partyId.has_value())
|
|
participant.partyId = trap.partyId.get();
|
|
participant.designator = trap.name;
|
|
participant.designator = trap.name;
|
|
participant.id = trap.id + 990000;
|
|
participant.sampleTime = trap.sampleTime;
|
|
participant.symbol = "AP6D|CMPL|10011500002104000000";
|
|
|
|
createPlayerSymbols(participant, sizeL);
|
|
Core::GeoPosition pos;
|
|
pos.lat = trap.impactPosition.lat;
|
|
pos.lon = trap.impactPosition.lon;
|
|
|
|
DEBUG_LOG(m_logger) << "Try create Booby trap with id " << trap.id << " internal id " << participant.id;
|
|
DEBUG_LOG(m_logger) << "Try create Booby trap with pos " << pos.lat << " " << pos.lon;
|
|
m_connection->playerCreate(participant.id, participant.partyId, buildPlayerSymbolFilePath(participant.id),
|
|
toRelative3D(pos), -1, -1, Core::PositionReportingSource::External);
|
|
m_connection->setPlayerVisible(participant.id, true);
|
|
}
|
|
|
|
void BuildingViewerDdsController::updatePlayerSymbols(const int sizeL, const int forParty)
|
|
{
|
|
if (!m_ppmDtc)
|
|
return;
|
|
|
|
auto cb = [this, sizeL, forParty](const ParticipantManagement::Participant& participant) {
|
|
if (!isValidParticipant(participant))
|
|
return;
|
|
|
|
if (forParty == -1 || participant.partyId == forParty)
|
|
createPlayerSymbols(participant, sizeL);
|
|
};
|
|
m_ppmDtc->participants(cb);
|
|
}
|
|
|
|
void BuildingViewerDdsController::createPlayerSymbols(const ParticipantManagement::Participant& participant,
|
|
const int sizeL)
|
|
{
|
|
SymbolInfo symbolinfo;
|
|
for (int i = 0; i < m_vecFlashColors.size(); ++i)
|
|
{
|
|
Q_ASSERT(participant.id != 0);
|
|
symbolinfo.participantId = participant.id;
|
|
symbolinfo.strPathToSymbolFile = buildPlayerSymbolFilePath(participant.id, i);
|
|
symbolinfo.color = m_vecFlashColors.at(i);
|
|
symbolinfo.useThisSymbolNow = !symbolinfo.color.isValid();
|
|
createSymbolResultWatcher(participant, symbolinfo, sizeL);
|
|
}
|
|
}
|
|
|
|
void BuildingViewerDdsController::createSymbolResultWatcher(const ParticipantManagement::Participant& participant,
|
|
const SymbolInfo& symbolInfo, const int sizeL)
|
|
{
|
|
AdvancedSymbology::SymbolResultWatcher* pSymbolWatcher = new AdvancedSymbology::SymbolResultWatcher(this);
|
|
|
|
bool symbolWatcherBenchmark = false;
|
|
if (symbolWatcherBenchmark)
|
|
{
|
|
pSymbolWatcher->m_et = new QElapsedTimer;
|
|
pSymbolWatcher->m_et->start();
|
|
}
|
|
|
|
bool writeParticipantSymbols = true;
|
|
if (writeParticipantSymbols)
|
|
{
|
|
QFile f(symbolInfo.strPathToSymbolFile);
|
|
if (!f.exists())
|
|
{
|
|
QPixmap pm(1, 1);
|
|
pm.fill(Qt::black);
|
|
pm.save(symbolInfo.strPathToSymbolFile, "PNG");
|
|
}
|
|
}
|
|
|
|
connect(pSymbolWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady, this,
|
|
[this, pSymbolWatcher, symbolInfo, writeParticipantSymbols, symbolWatcherBenchmark]() {
|
|
std::shared_ptr<int> symbolWatcherDeleter(
|
|
nullptr, [this, pSymbolWatcher, symbolWatcherBenchmark](int*) {
|
|
if (symbolWatcherBenchmark && pSymbolWatcher->m_et != nullptr)
|
|
DEBUG_LOG(m_logger) << "Symbol creation took " << pSymbolWatcher->m_et->elapsed() << " ms";
|
|
|
|
pSymbolWatcher->deleteLater();
|
|
});
|
|
(void)symbolWatcherDeleter;
|
|
|
|
if (!pSymbolWatcher->symbolResult()->symbolImage().isValid())
|
|
{
|
|
WARNING_LOG(m_logger) << getSessionInfo()
|
|
<< "'slot_SymbolWatcherResultReady': Symbol creation failed -> "
|
|
"'!symbolImage.isValid()', BgViewer might not display symbol(s)!";
|
|
return;
|
|
}
|
|
|
|
int32_t playerID = symbolInfo.participantId;
|
|
|
|
if (writeParticipantSymbols)
|
|
if (!pSymbolWatcher->symbolResult()->symbolImage().image().save(symbolInfo.strPathToSymbolFile))
|
|
{
|
|
WARNING_LOG(m_logger)
|
|
<< getSessionInfo() << "Could not write symbol file for BgViewer: \""
|
|
<< symbolInfo.strPathToSymbolFile << "\", BgViewer might not display symbol(s)!";
|
|
return;
|
|
}
|
|
|
|
if (symbolInfo.useThisSymbolNow)
|
|
m_connection->playerUpdatePlayerSymbol(playerID, symbolInfo.strPathToSymbolFile);
|
|
});
|
|
|
|
pSymbolWatcher->setSymbolResult(m_asbIfc->findImage(participant, sizeL, symbolInfo.color));
|
|
}
|
|
|
|
void BuildingViewerDdsController::reCheckHitLine()
|
|
{
|
|
if (m_ppmDtc && m_ppmDtc->isHitLineInfoEnabled())
|
|
{
|
|
m_ppmDtc->hitLineInfos([this](const ParticipantManagement::HitLineInfo& hli) {
|
|
onHitLineInfoChanged(hli, DataModification::eAdded);
|
|
});
|
|
}
|
|
|
|
if (m_bldDtc && m_bldDtc->isBuildingHitLineInfoEnabled())
|
|
{
|
|
QMap<qint32, Buildings::BuildingHitLineInfo> hitMap = m_bldDtc->buildingHitLineInfos();
|
|
|
|
for (const auto& hli : hitMap)
|
|
{
|
|
if (hli.sampleTime < (m_session->partitionTime() - 3000))
|
|
onBuildingHitLineInfoChanged(hli, DataModification::eAdded);
|
|
}
|
|
}
|
|
}
|
|
|
|
QString BuildingViewerDdsController::buildPlayerSymbolFilePath(int playerID, int postfix)
|
|
{
|
|
return gStrSymbolFolderPath() + QStringLiteral("symbol_player_%1_%2.png").arg(playerID).arg(postfix);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onSynchronizeSimulationTime()
|
|
{
|
|
if (m_session != nullptr)
|
|
{
|
|
|
|
m_connection->systemSetSimulationTime(m_session->partitionTime());
|
|
}
|
|
else
|
|
WARNING_LOG(m_logger)
|
|
<< getSessionInfo()
|
|
<< "'slotSynchronizeSimulationTime': m_session is nullptr, could not query partitionTime!";
|
|
}
|
|
|
|
void BuildingViewerDdsController::updateBuildingRoomNames(
|
|
const InfrastructureManagement::MainStructureElement& building)
|
|
{
|
|
|
|
const std::vector<InfrastructureManagement::StructureElement>& vecElements = building.elements;
|
|
|
|
int32_t buildingDatabaseId = building.id;
|
|
QString featureID = BuildingViewer::databaseId2FeatureId(buildingDatabaseId);
|
|
for (const auto& se : vecElements)
|
|
{
|
|
if (se.structureElementTypeId == InfrastructureManagement::StructureElementType::Building)
|
|
m_connection->excSetBuildingDisplayName(featureID, QString::fromStdString(se.displayName));
|
|
if (se.structureElementTypeId == InfrastructureManagement::StructureElementType::Room)
|
|
m_connection->excSetRoomDisplayName(featureID, QString::number(se.mountingPointId),
|
|
QString::fromStdString(se.displayName));
|
|
}
|
|
}
|
|
|
|
void BuildingViewerDdsController::deleteParticipant(int participantId)
|
|
{
|
|
m_connection->playerDelete(participantId);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onPlayerPicked(int playerId)
|
|
{
|
|
if (m_ppmGuiIfc)
|
|
m_ppmGuiIfc->showParticipantInfoWindow(playerId, true);
|
|
else
|
|
INFO_LOG(m_logger) << "No m_ppmGuiIfc";
|
|
}
|
|
|
|
void BuildingViewerDdsController::effectGenerationUnitPicked(int structureElementId)
|
|
{
|
|
if (m_blgGuiIfc)
|
|
m_blgGuiIfc->showEffectGenerationUnitWindow(structureElementId);
|
|
else
|
|
INFO_LOG(m_logger) << "No m_blgGuiIfc";
|
|
}
|
|
|
|
void BuildingViewerDdsController::onAssetPicked(int structureElementId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "User clicked on asset with structureElementId " << structureElementId << " from "
|
|
<< QString::number((quintptr)m_session->adr(), 16);
|
|
|
|
if (m_session->isReplayPartition())
|
|
return;
|
|
|
|
if (strcmp(PROJECTBUILD, "guez") == 0 && LtsSessionData::LtsSessionData::getInstance()->isTablet())
|
|
return;
|
|
auto it = m_mountingPointId2AssetType.find(structureElementId);
|
|
if (it == m_mountingPointId2AssetType.end())
|
|
{
|
|
WARNING_LOG(m_logger) << "onAssetPicked: User clicked on asset mounted to structureElementId "
|
|
<< structureElementId << " which was not added beforehand?! Ignoring...";
|
|
return;
|
|
}
|
|
|
|
const BuildingViewer::AssetTypes assetType = it.value();
|
|
switch (assetType)
|
|
{
|
|
case BuildingViewer::AssetTypes::AnnouncementUnit:
|
|
DEBUG_LOG(m_logger) << "Clicked asset is of type is AnnouncementUnit";
|
|
if (m_avmIfc)
|
|
m_avmIfc->showAnnouncementUnit(structureElementId);
|
|
return;
|
|
|
|
case BuildingViewer::AssetTypes::EffectGenerationUnit:
|
|
DEBUG_LOG(m_logger) << "Clicked asset is of type is EffectGenerationUnit";
|
|
effectGenerationUnitPicked(structureElementId);
|
|
return;
|
|
|
|
case BuildingViewer::AssetTypes::IndoorCameraWithSpeaker:
|
|
DEBUG_LOG(m_logger) << "Clicked asset is of type is IndoorCameraWithSpeaker";
|
|
if (m_avmIfc)
|
|
{
|
|
m_avmIfc->showCamera(m_irreplacableWidget, structureElementId);
|
|
m_avmIfc->showAnnouncementUnit(structureElementId);
|
|
}
|
|
return;
|
|
|
|
case BuildingViewer::AssetTypes::IndoorCamera:
|
|
DEBUG_LOG(m_logger) << "Clicked asset is of type is IndoorCamera";
|
|
if (m_avmIfc)
|
|
m_avmIfc->showCamera(m_irreplacableWidget, structureElementId);
|
|
return;
|
|
}
|
|
WARNING_LOG(m_logger) << "onAssetPicked: BuildingViewer::AssetTypes is unknown: " << assetType;
|
|
}
|
|
|
|
void BuildingViewerDdsController::onMountingPointChanged(const InfrastructureManagement::MountingPoint& data,
|
|
DataModification modType)
|
|
{
|
|
handleMountingPoint(data, modType);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onBuildingStateChanged(const Buildings::BuildingState& data, DataModification modType)
|
|
{
|
|
if (modType != DataModification::eAdded && modType != DataModification::eModified)
|
|
return;
|
|
|
|
QMap<int, int> roomDestructions;
|
|
for (const Buildings::RoomState& rs : data.roomStates)
|
|
{
|
|
roomDestructions.insert(rs.mountingPointId, rs.damagePercent);
|
|
}
|
|
|
|
m_connection->buildingHit(data.id, data.id, data.damagePercent, roomDestructions);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onBuildingHitLineInfoChanged(const Buildings::BuildingHitLineInfo& hli,
|
|
DataModification modType)
|
|
{
|
|
int topidId = static_cast<int>(hli.id);
|
|
|
|
DEBUG_LOG(m_logger) << "onBuildingHitLineInfoChanged: " << hli;
|
|
|
|
const QString& strFeatureID = BuildingViewer::databaseId2FeatureId(hli.buildingId);
|
|
if (!m_viewerInterface->isViewOpen(strFeatureID.toStdString()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "building not opened: hli.buildingId()=" << hli.buildingId;
|
|
return;
|
|
}
|
|
|
|
const bool isRemove = (modType == DataModification::eRemoved);
|
|
DEBUG_LOG(m_logger) << "isRemove=" << isRemove;
|
|
|
|
if (isRemove)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onBuildingHitLineInfoChanged: remove ";
|
|
QVector<BuildingHit>& bhl = m_buildingHits[hli.buildingId];
|
|
|
|
auto it = std::find_if(bhl.begin(), bhl.end(), [hli](const BuildingHit& bh) { return (bh.id == hli.id); });
|
|
|
|
if (it != bhl.end())
|
|
{
|
|
DEBUG_LOG(m_logger) << "bhl.erase(it)";
|
|
bhl.erase(it);
|
|
}
|
|
m_connection->removeRoomHit(hli.id);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
BuildingHit bh;
|
|
bh.id = hli.id;
|
|
bh.sampleTime = hli.sampleTime;
|
|
bh.originatorParticipantId = hli.originatorParticipantId;
|
|
bh.chargeTypeId = hli.chargeTypeId;
|
|
|
|
bool contains = false;
|
|
for (const auto& it : m_buildingHits[hli.buildingId])
|
|
{
|
|
if (it.id == bh.id)
|
|
contains = true;
|
|
}
|
|
if (!contains)
|
|
{
|
|
|
|
m_buildingHits[hli.buildingId].push_back(bh);
|
|
}
|
|
}
|
|
int lineType = 2;
|
|
|
|
m_connection->roomHit(topidId, hli.originatorParticipantId, hli.buildingId, hli.hitOnRoomId,
|
|
toRelative3D(hli.originatorPos), lineType, isRemove ? 0 : hli.sampleTime);
|
|
}
|
|
|
|
void BuildingViewerDdsController::onCleanupBuildingHitlines()
|
|
{
|
|
DEBUG_LOG(m_logger) << "onCleanupBuildingHitlines: ";
|
|
const qint64 tNow = QDateTime::currentMSecsSinceEpoch();
|
|
const auto isOutdated = [tNow](const BuildingHit& bh) {
|
|
return LtsUtils::isPositionTimeOutdated(tNow, bh.sampleTime);
|
|
};
|
|
for (auto& hitLines : m_buildingHits)
|
|
hitLines.erase(std::remove_if(hitLines.begin(), hitLines.end(), isOutdated), hitLines.end());
|
|
}
|
|
|
|
void BuildingViewerDdsController::onMainStructureElementChanged(
|
|
const InfrastructureManagement::MainStructureElement& mse, DataModification modType)
|
|
{
|
|
if (!isBuildingKnown(mse.id))
|
|
return;
|
|
|
|
switch (modType)
|
|
{
|
|
case DataModification::eAdded:
|
|
case DataModification::eModified: {
|
|
if (mse.modelId != 0 && m_ismDtc->isElementAssignedToExercise(mse, m_exerciseId))
|
|
{
|
|
DEBUG_LOG(m_logger) << "m_buildingHeight.insert id=" << mse.id << " z=" << mse.geoPosition.z;
|
|
m_buildingHeight.insert(mse.id, mse.geoPosition.z);
|
|
QHash<int, int> roomId2RoomNumber;
|
|
for (const InfrastructureManagement::StructureElement& se : mse.elements)
|
|
{
|
|
if (se.structureElementTypeId != InfrastructureManagement::StructureElementType::Room)
|
|
continue;
|
|
roomId2RoomNumber[se.id] = se.mountingPointId;
|
|
}
|
|
m_roomId2RoomNumber[mse.id] = roomId2RoomNumber;
|
|
|
|
updateBuildingRoomNames(mse);
|
|
}
|
|
break;
|
|
}
|
|
case DataModification::eRemoved:
|
|
m_roomId2RoomNumber.remove(mse.id);
|
|
m_buildingHeight.remove(mse.id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
QString BuildingViewerDdsController::getSessionInfo() const
|
|
{
|
|
if (m_session == nullptr)
|
|
return QStringLiteral("session == nullptr: ");
|
|
|
|
return m_session->toString() + QStringLiteral(": ");
|
|
}
|
|
|
|
void BuildingViewerDdsController::onFireEventFlashTimer()
|
|
{
|
|
static int currFlashState = 0;
|
|
|
|
if (m_session == nullptr)
|
|
{
|
|
DEBUG_LOG(m_logger) << getSessionInfo()
|
|
<< "onFireEventFlashTimer: Session is not available, flashing not possible!";
|
|
return;
|
|
}
|
|
|
|
++currFlashState;
|
|
|
|
if (currFlashState >= m_vecFlashColors.size())
|
|
currFlashState = 0;
|
|
|
|
qint64 currTime_ms = m_session->partitionTime();
|
|
int gearFlashTime_ms = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("GearFlashTime_s"), 10)
|
|
.toInt() *
|
|
1000;
|
|
int maxGearFlashTime_ms = LtsUtils::OperatorSettings::getInstance()
|
|
->value(QStringLiteral("BgViewer"), QStringLiteral("maxGearFlashTime_s"), 120)
|
|
.toInt() *
|
|
1000;
|
|
|
|
auto itB = m_participantGearEvents.begin();
|
|
while (itB != m_participantGearEvents.end())
|
|
{
|
|
const int playerID = itB.key();
|
|
qint64 eventStartTime_ms = itB.value();
|
|
|
|
qint64 diff_ms = currTime_ms - eventStartTime_ms;
|
|
|
|
if (diff_ms > gearFlashTime_ms)
|
|
{
|
|
if (diff_ms > maxGearFlashTime_ms)
|
|
itB = m_participantGearEvents.erase(itB);
|
|
else
|
|
++itB;
|
|
|
|
m_connection->playerUpdatePlayerSymbol(playerID, buildPlayerSymbolFilePath(playerID, cColorSymbolId));
|
|
continue;
|
|
}
|
|
|
|
m_connection->playerUpdatePlayerSymbol(playerID, buildPlayerSymbolFilePath(playerID, currFlashState));
|
|
++itB;
|
|
}
|
|
|
|
for (auto it = m_lastPositionTime.cbegin(); it != m_lastPositionTime.cend(); ++it)
|
|
{
|
|
const int playerId = it.key();
|
|
const qint64 sampleTime = it.value();
|
|
if (isOutdatedPosition(sampleTime))
|
|
m_connection->setPlayerVisible(playerId, false);
|
|
}
|
|
}
|
|
|
|
} // namespace BuildingViewer
|