1691 lines
55 KiB
C++
Executable File
1691 lines
55 KiB
C++
Executable File
|
|
|
|
|
|
#include "bdv_dds_controller.h"
|
|
|
|
#include <Plugins/InfrastructureManagement/Shared/ism_mounting_point.h>
|
|
#include <InfrastructureManagement/ism_dtc.h>
|
|
#include <LtsSession/lts_session_data.h>
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <LtsUtilsCore/outdated_position.h>
|
|
#include <LtsUtilsSettings/settings.h>
|
|
#include <ngltools/ngltools_filesystem.h>
|
|
#include "bdv_helper.h"
|
|
#include "bdv_plugin_provider.h"
|
|
#include "bdv_controller.h"
|
|
#include "logger.h"
|
|
#include "bdv_communication_interface_internal.h"
|
|
|
|
#include <QDateTime>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonParseError>
|
|
#include <QPixmap>
|
|
#include <QTimer>
|
|
#include <QXmlStreamWriter>
|
|
|
|
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(getLogger("BuildingViewerDdsController"))
|
|
{}
|
|
|
|
|
|
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);
|
|
sw.setCodec("UTF-8");
|
|
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 : qAsConst(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 : qAsConst(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().underlying()) {
|
|
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().underlying())
|
|
{
|
|
case InfrastructureManagement::MountingPointType_def::AnnouncementUnit:
|
|
assetType = BuildingViewer::AssetTypes::AnnouncementUnit;
|
|
strAssetType = QStringLiteral("AnnouncementUnit");
|
|
break;
|
|
case InfrastructureManagement::MountingPointType_def::EffectGenerationControlUnit:
|
|
assetType = BuildingViewer::AssetTypes::EffectGenerationUnit;
|
|
strAssetType = QStringLiteral("EffectGenerationControlUnit");
|
|
break;
|
|
case InfrastructureManagement::MountingPointType_def::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 : qAsConst(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_def::SupportUnit ||
|
|
tc == ParticipantManagement::TypeClass_def::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());
|
|
|
|
lineType = m_pdmDtc->getChargeTypeSpecialProperty(hli.chargeTypeId(),0,0).underlying();
|
|
|
|
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().is_set())
|
|
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_def::Building)
|
|
m_connection->excSetBuildingDisplayName(featureID, QString::fromStdString(se.displayName()));
|
|
if (se.structureElementTypeId() == InfrastructureManagement::StructureElementType_def::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);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|