#include "bdv_dds_controller.h" #include "bdv_communication_interface_internal.h" #include "bdv_controller.h" #include "bdv_helper.h" #include "bdv_plugin_provider.h" #include "logger.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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& 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 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 : 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 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 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 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 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& 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 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 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& 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 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(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& 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 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