/** * Handler for player data * $Id$ * $HeadURL$ * $Author$ * Created by hjl, SLSE, 2017-02-20 */ #include "handler_aes_data.h" #include #include #include #include #include #include #include #include #include #include #define TIMEDIFFVALID 3000 namespace CesBuildings { HandlerAesData::HandlerAesData(const std::shared_ptr &ddsHandler, const std::shared_ptr &buildingDataHandler, QObject *parent) : QObject(parent) , m_ddsHandler(ddsHandler) , m_handlerBuildingData(buildingDataHandler) , m_logger(CentralEffectSimulation::getLogger("Plugin.Buildings.HandlerAesData")) { } // end of ctor() //----------------------------------------------------------------------------- HandlerAesData::~HandlerAesData() { } // end of dtor() //----------------------------------------------------------------------------- bool HandlerAesData::init() { auto dtcAes = m_ddsHandler->dtcAes(); dtcAes->enableChargeEffect(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::chargeEffectChanged, this, &HandlerAesData::onChargeEffectChanged); dtcAes->enableExplosionSingle(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged, this, &HandlerAesData::onExplosionSingleChanged); dtcAes->enableAirSupportImpact(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::airSupportImpactChanged, this, &HandlerAesData::onAirSupportImpactChanged); dtcAes->enableTriggeredImpacts(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged, this, &HandlerAesData::onTriggeredImpactsChanged); dtcAes->waitForHistoricalDataAesItemView(1000); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged, this, &HandlerAesData::onAesItemViewChanged); dtcAes->enableEffects(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::effectsChanged, this, &HandlerAesData::onEffectsChanged, Qt::UniqueConnection); dtcAes->enableBoobyTrap(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged, this, &HandlerAesData::onBoobyTrapChanged, Qt::UniqueConnection); // Get 'static' data for all AesItemView { auto cb = [this](const AreaEffectSimulation::AesItemView & aiv) { onAesItemViewChanged(aiv, DataModification::eAdded); }; if (!dtcAes->aesItemViews(cb)) return false; } return true; } // end of init() void HandlerAesData::onBoobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType) { DEBUG_LOG(m_logger) << "onBoobyTrapChanged" << " id=" << data.id() << " impactPosition=" << data.impactPosition() << " impactRadius=" << data.impactRadius() << " effectStateId=" << data.effectStateId(); if (data.isDeleted() || data.effectStateId() == AreaEffectSimulation::AesEffectState::EsRemoved || modType == DataModification::eRemoved) return; if (!data.triggered()) return; INFO_LOG(m_logger) << "HandlerParticipantBoobyTrap::onBoobyTrapChanged" << " data.id=" << data.id() << " effectStateId=" << data.effectStateId(); if (!data.effectStart().is_set()) return; if ((m_ddsHandler->currentPartitionTime() - (data.effectStart().get() + data.effectDuration())) > 2000) { INFO_LOG(m_logger) << "onBoobyTrap outdated"; return; } AreaEffectSimulation::AesItemView itemView; AreaEffectSimulation::AesType aesType = AreaEffectSimulation::AesType_def::AtBoobyTrap; m_ddsHandler->dtcAes()->aesItemViewFromEntityId(data.id(), aesType, itemView); checkBuildingsHit(itemView); } //----------------------------------------------------------------------------- // /////////////////////////////////// // A E S (AreaEffectSimulation) // /////////////////////////////////// void HandlerAesData::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType) { DEBUG_LOG(m_logger) << "onAesItemViewChanged" << " data.aesTypeId()=" << data.aesTypeId(); if (modType != DataModification::eAdded && modType != DataModification::eModified) return; DEBUG_LOG(m_logger) << "Got AesItemView: " << data.effectStateId(); if (data.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired) { INFO_LOG(m_logger) << "AesEffectState_def::EsExpired" << " currentPartitionTime=" << m_ddsHandler->currentPartitionTime() << " effectStart=" << data.effectStart() << " diff = " << m_ddsHandler->currentPartitionTime() - data.effectStart() ; if (m_ddsHandler->currentPartitionTime() == 0) return; if ((data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary) || (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary)) { if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary) { DEBUG_LOG(m_logger) << "AtOffRouteMineSecondary id=" << data.id() / 1000; auto offRouteMine = m_ddsHandler->dtcAes()->offRouteMine((int)(data.id() / 1000)); if (!offRouteMine.effectStart().is_set()) { DEBUG_LOG(m_logger) << "!offRouteMine.effectStart().is_set()"; return; } DEBUG_LOG(m_logger) << " currentPartitionTime=" << m_ddsHandler->currentPartitionTime() << " effectStart=" << offRouteMine.effectStart() << " effectDuration=" << offRouteMine.effectDuration() << " diff = " << m_ddsHandler->currentPartitionTime() - (offRouteMine.effectStart().get() + offRouteMine.effectDuration()) ; if ((m_ddsHandler->currentPartitionTime() - (offRouteMine.effectStart().get() + offRouteMine.effectDuration())) < 2000) { DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary Mine wird ausgeloeset."; checkBuildingsHit(data); } else { DEBUG_LOG(m_logger) << "outdated return"; return; } } if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary) { DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary id=" << data.id() / 1000; //auto offRouteMine = m_ddsHandler->dtcAes()->offRouteMine((int) (data.id() / 1000)); auto boobyTrap = m_ddsHandler->dtcAes()->boobyTrap((int)(data.id() / 1000)); if (!boobyTrap.effectStart().is_set()) { DEBUG_LOG(m_logger) << "!offRouteMine.effectStart().is_set()"; return; } DEBUG_LOG(m_logger) << " currentPartitionTime=" << m_ddsHandler->currentPartitionTime() << " effectStart=" << boobyTrap.effectStart() << " effectDuration=" << boobyTrap.effectDuration() << " diff = " << m_ddsHandler->currentPartitionTime() - (boobyTrap.effectStart().get() + boobyTrap.effectDuration()) ; if ((m_ddsHandler->currentPartitionTime() - (boobyTrap.effectStart().get() + boobyTrap.effectDuration())) < 2000) { DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary Mine wird ausgeloeset."; checkBuildingsHit(data); } else { DEBUG_LOG(m_logger) << "outdated return"; return; } } DEBUG_LOG(m_logger) << "removeStates data"; removeStates(data); DEBUG_LOG(m_logger) << "m_aesItemViewsUtm remove id" << data.aesItemId(); m_aesItemViewsUtm.remove(data.aesItemId()); } } } void HandlerAesData::removeStates(const AreaEffectSimulation::AesItemView &data) { if (data.aesTypeId() != AreaEffectSimulation::AesType_def::AtExplosionArea) return; DEBUG_LOG(m_logger) << "Remove aesItemId=" << data.aesItemId(); // remove State for each building for (CentralEffectSimulation::BitfieldStates &bfs : m_bitfieldStatesBld) bfs.remove(data.aesItemId()); } // end removeStates void HandlerAesData::calculateBuildingExplosion(const AreaEffectSimulation::ChargeStruct &chargeStruct, const Core::GeoPosition2D& impactPosition, const int id) { const double impactRadius = m_ddsHandler->chargeTypeRadius(chargeStruct); DEBUG_LOG(m_logger) << "Handle ExplosionSingle (EsActivated)" << ", m_handlerBuildingData.getFootprints().size =" << m_handlerBuildingData->getFootprints().size(); QHash footprints = m_handlerBuildingData->getFootprints(); // Bounding-Box-Check // QMap m_handlerBuildingData.getFootprints(); for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint : qAsConst(footprints)) { const Core::GeoPosition2D impactPosition2D(impactPosition.lon(), impactPosition.lat()); if (inFootprint(impactPosition, footprint, impactRadius)) { DEBUG_LOG(m_logger) << "ftc sendCESHitOnBuilding mseId = " << footprint.mseId << " chargeTypeId = " << chargeStruct.chargeTypeId() << " id = " << id << " currentPartitionTime = " << m_ddsHandler->currentPartitionTime() << " impactPosition2D.lon = " << impactPosition2D.lon() << " impactPosition2D.lat = " << impactPosition2D.lat(); Buildings::SendCesHitOnBuildingRequest requestBld( footprint.mseId, chargeStruct.chargeTypeId(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(chargeStruct.chargeTypeId()).destructiveFactor(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(chargeStruct.chargeTypeId()).penetrationFactor(), id, m_ddsHandler->currentPartitionTime(), Core::GeoPosition(impactPosition2D.lon(), impactPosition2D.lat(), 0.0), QStringLiteral("properties").toStdString()); m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this); } else DEBUG_LOG(m_logger) << "Not sendCESHitOnBuilding mseId = " << footprint.mseId; DEBUG_LOG(m_logger) << "boundingBoxImpact.contains(footprint)"; } // for } void HandlerAesData::startTimer(int id, int msec) { auto *timer = new QTimer(this); timer->setProperty("chargeEffectId", id); // Timeout-Slot mit echtem SLOT connect(timer, &QTimer::timeout, this, &HandlerAesData::onStartTimeReached); timer->start(msec); DEBUG_LOG(m_logger) << "Timer " << id << " gestartet fuer " << msec << " ms."; } void HandlerAesData::onStartTimeReached() { if (!sender()) return; bool bOk; int chargeEffectId = sender()->property("chargeEffectId").toInt(&bOk); if (!bOk) return; DEBUG_LOG(m_logger) << "Timer " << chargeEffectId << " ausgeloest!"; // Timer stoppen und loeschen AreaEffectSimulation::ChargeEffect data = m_chargeEffects.value(chargeEffectId); AreaEffectSimulation::ChargeStruct chargeStruct(data.chargeTypeId(),data.igniterTypeId(), data.igniterFunctionId()); calculateBuildingExplosion(chargeStruct, data.position(), data.id()); sender()->deleteLater(); m_chargeEffects.remove(chargeEffectId); } void HandlerAesData::onChargeEffectChanged(const AreaEffectSimulation::ChargeEffect &data, DataModification modType) { DEBUG_LOG(m_logger) << "HandlerExplosions::onChargeEffectChanged"; if (modType == DataModification::eRemoved) return; const long long effectStart = data.effectStart(); DEBUG_LOG(m_logger) << "HandlerExplosions::onChargeEffectChanged" << ", effectStart=" << effectStart << ", currentPartitionTime=" << m_ddsHandler->currentPartitionTime(); // Startzeit in der Vergangenheit: aktuelle Zeit - 3 Sekunden if ( effectStart < m_ddsHandler->currentPartitionTime() - TIMEDIFFVALID ) { DEBUG_LOG(m_logger) << "Startzeit in der Vergangenheit: aktuelle Zeit - 3 Sekunden"; return; } // Startzeit entspricht der aktuellen Zeit if ( effectStart > m_ddsHandler->currentPartitionTime() - TIMEDIFFVALID && effectStart < m_ddsHandler->currentPartitionTime() + data.effectDuration() ) { DEBUG_LOG(m_logger) << "Startzeit entspricht der aktuellen Zeit"; AreaEffectSimulation::ChargeStruct chargeStruct; chargeStruct.chargeTypeId(data.chargeTypeId()); chargeStruct.igniterTypeId(data.igniterTypeId()); calculateBuildingExplosion(chargeStruct, data.position(), data.id()); } // Startzeit in der Zukunft: else if (effectStart > m_ddsHandler->currentPartitionTime() + data.effectDuration()) { // Start mit Timer DEBUG_LOG(m_logger) << "Startzeit in der Zukunft" << ", id :" << data.id() << ", duration=" << effectStart - m_ddsHandler->currentPartitionTime(); m_chargeEffects.insert(data.id(), data); startTimer(data.id(), effectStart - m_ddsHandler->currentPartitionTime()); } DEBUG_LOG(m_logger) << "END onChargeEffectChanged"; } void HandlerAesData::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType) { bool handleExplosion = false; if (modType != DataModification::eAdded && modType != DataModification::eModified) { m_explosionSingleState.remove(data.id()); return; } if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved) { DEBUG_LOG(m_logger) << "Remove SingleExplosion"; m_explosionSingleState.remove(data.id()); return; } // Only handle transitions from prepared or fired to activated if ((m_explosionSingleState.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsPrepared || m_explosionSingleState.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsFired) && data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated) { DEBUG_LOG(m_logger) << "Handle SingleExplosion"; handleExplosion = true; } m_explosionSingleState.insert(data.id(), data.effectStateId()); DEBUG_LOG(m_logger) << "Got ExplosionSingle"; if (handleExplosion) { calculateBuildingExplosion(data.chargeStruct(), data.impactPosition(), data.id()); } } // onExplosionSingleChanged void HandlerAesData::onAirSupportImpactChanged(const AreaEffectSimulation::AirSupportImpact &data, DataModification modType) { DEBUG_LOG(m_logger) << "Got onAirSupportImpactChanged()"; AreaEffectSimulation::ExplosionSingle esData; esData.id(data.id()); esData.chargeStruct().chargeTypeId(data.chargeStruct().chargeTypeId()); esData.impactPosition(data.impactPosition()); esData.effectStateId(data.effectStateId()); onExplosionSingleChanged(esData, modType); } void HandlerAesData::onTriggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType) { if (modType != DataModification::eAdded && modType != DataModification::eModified) return; DEBUG_LOG(m_logger) << "Got TriggeredImpacts"; preCalculateImpacts(data); } void HandlerAesData::onEffectsChanged(const AreaEffectSimulation::Effects &effects, DataModification modType) { TRACE_LOG(m_logger) << " Got Effects " << effects; if (modType == DataModification::eRemoved) return; if (effects.aesTypeId().underlying() != AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions) return; DEBUG_LOG(m_logger) << " Got typechecked effects " << effects; // calculate for every building of our exercise: const QHash &footprints = m_handlerBuildingData->getFootprints(); for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint : qAsConst(footprints)) { calculateImpacts(effects, footprint); } } // /////////////////////////////////// // I S M (InfrastructureManagement) // /////////////////////////////////// // /////////////////////////////////// // Calculations: // /////////////////////////////////// bool HandlerAesData::inBoundingBox(const Core::GeoPosition2D& impactPosition, const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint, const double impactRadius) const { const QPointF impactPos(CentralEffectSimulation::geographic2Utm(impactPosition, footprint.zone)); const QPolygonF polyBldgRadius({QPointF(footprint.boundingBoxPolyUtm.bottomLeft().rx() - impactRadius, footprint.boundingBoxPolyUtm.bottomLeft().ry() + impactRadius), QPointF(footprint.boundingBoxPolyUtm.topRight().rx() + impactRadius, footprint.boundingBoxPolyUtm.bottomLeft().ry() + impactRadius), QPointF(footprint.boundingBoxPolyUtm.topRight().rx() + impactRadius, footprint.boundingBoxPolyUtm.topRight().ry() - impactRadius), QPointF(footprint.boundingBoxPolyUtm.bottomLeft().rx() - impactRadius, footprint.boundingBoxPolyUtm.topRight().ry() - impactRadius)}); return (polyBldgRadius.containsPoint(impactPos, Qt::OddEvenFill)); } bool HandlerAesData::inFootprint(const Core::GeoPosition2D &geoPosition, const CentralEffectSimulation::HandlerBuildingData::Footprint &footPrint, const double radius) const { if ( !inBoundingBox( geoPosition, footPrint, radius)) return false; const QPointF impact(CentralEffectSimulation::geographic2Utm(geoPosition, footPrint.zone)); // NT: Folgendes raubt zu viele Zeilen beim tail. Daher nehme ich es raus. // if (m_logger.isDebugEnabled()) // { // const QPointF utmBuilding(CentralEffectSimulation::geographic2Utm(footPrint.geoPosition, footPrint.zone)); // DEBUG_LOG(m_logger) << "Impact (x/y)=" << CentralEffectSimulation::debug(impact - utmBuilding); // for (int i = 0; i < footPrint.polyUtm.length(); i++) // DEBUG_LOG(m_logger) << " poly (x/y)=" << CentralEffectSimulation::debug(footPrint.polyUtm[i] - utmBuilding); // } // Check, if hit direct on building if (footPrint.polyUtm.containsPoint(impact, Qt::OddEvenFill)) return true; // Check, if hit in munition-radius QPainterPath ppFp; ppFp.addPolygon(footPrint.polyUtm); QPainterPath ppCircle; ppCircle.addEllipse(impact, radius, radius); return ppFp.intersects(ppCircle); } void HandlerAesData::preCalculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data) { if (data.id() <= 0) return; DEBUG_LOG(m_logger) << "preCalculateImpacts"; // Find AES ItemView and set Bounding-Box of Aes-Field const int aesItemId = data.aesItemId(); // Search corresponding itemView. If not available no impact to participant. // Use dtcAes since sender (AES) publishes TriggeredImpacts and AesItemView // AreaEffectSimulation::AesItemView aesItemView; std::shared_ptr dtcAes = m_ddsHandler->dtcAes(); if (!dtcAes->aesItemViewFromEntityId(aesItemId, data.aesTypeId(), aesItemView)) { static long msgCnt = 0; WARNING_LOG(m_logger) << "TriggeredExplosions.aesItemId=" << data.aesItemId() << " No aesItemView available" << " (cnt=" << ++msgCnt << ")"; return; } // Maybe ExlosionClusters Trajectory is hitting a building? // Vulnerability should be calculated const QHash &footprints = m_handlerBuildingData->getFootprints(); for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint : qAsConst(footprints)) { calculateImpacts(data, aesItemView, footprint); } // for } void HandlerAesData::onSendCesHitOnBuildingReply(const Core::FunctionReplyObject &replyObject) { if (replyObject.error()) WARNING_LOG(m_logger) << "Can not send hit on building to building service: " << replyObject() << ", m_handlerBuildingData.getFootprints().size()" << m_handlerBuildingData->getFootprints().size(); else DEBUG_LOG(m_logger) << "onSendCesHitOnBuildingReply for id " << replyObject.id() << " success\n" << " replyObject.str() = " << replyObject.str(); } void HandlerAesData::onSendCesHit3DonBuildingReply(const Core::FunctionReplyObject &replyObject) { DEBUG_LOG(m_logger) << "m_handlerBuildingData.getFootprints() size " << m_handlerBuildingData->getFootprints().size(); if (replyObject.error()) WARNING_LOG(m_logger) << "Can not send hit3D on building to building service: " << replyObject() << ", m_handlerBuildingData.getFootprints().size()" << m_handlerBuildingData->getFootprints().size(); else DEBUG_LOG(m_logger) << "onSendCesHit3DonBuildingReply for id " << replyObject.id() << " success\n" << " replyObject.str() = " << replyObject.str(); } void HandlerAesData::onSendCesHitInformationReply(const Core::FunctionReplyObject &reply) { if (reply.error()) WARNING_LOG(m_logger) << "onSendCesHitInformationReply for id " << reply.id() << " failed: " << reply.str(); else DEBUG_LOG(m_logger) << "onSendCesHitInformationReply for id " << reply.id() << " successful"; } void HandlerAesData::onSetBuildingStatesReply(const Core::FunctionReplyObject &reply) { if (reply.error()) WARNING_LOG(m_logger) << "onSetBuildingStatesReply for id " << reply.id() << " failed: " << reply.str(); else DEBUG_LOG(m_logger) << "onSetBuildingStatesReply for id " << reply.id() << " successful"; } // calculate impacts for one building void HandlerAesData::calculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data, const AreaEffectSimulation::AesItemView &aesItemView, const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint) { const int aesItemId = data.aesItemId(); bool bIsExplosionCluster = false; ProjectDataManagement::IgniterFunctionBase ignFuncBase = ProjectDataManagement::IgniterFunctionBase::IfbImpact; // check for correct AES type artillery only static QVector s_relevantTypes = { AreaEffectSimulation::AesType_def::AtExplosionArea, AreaEffectSimulation::AesType_def::AtExplosionSingle, AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions, }; if (data.aesTypeId().underlying() == AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions) { bIsExplosionCluster = true; ignFuncBase = igniterFunctionBaseExplosionCluster(aesItemId); // assume that fuseType does not differ within a single ExplosionArea DEBUG_LOG(m_logger) << "Calculate, bIsExplosionCluster = " << bIsExplosionCluster << ", ignFuncBase = " << ignFuncBase; } else if (s_relevantTypes.contains(data.aesTypeId().underlying())) DEBUG_LOG(m_logger) << "Calculate"; else { DEBUG_LOG(m_logger) << "No Calculation"; return; } if (aesItemId != aesItemView.aesItemId()) { WARNING_LOG(m_logger) << "mismatch: " << ", TriggeredExplosions.aesItemId=" << data.aesItemId() << ", AesItemView.aesItemId=" << aesItemView.aesItemId(); return; } DEBUG_LOG(m_logger) << "calculateImpacts mseId=" << footprint.mseId << " aesItemId " << aesItemId << ", bIsExplosionCluster = " << bIsExplosionCluster; // get position(s) of impact(s): CentralEffectSimulation::BitfieldStates &bitfieldStates = m_bitfieldStatesBld[footprint.mseId]; QSet &bitfieldState = bitfieldStates[aesItemId]; for (int i : qAsConst(bitfieldState)) DEBUG_LOG(m_logger) << "bitfieldState for " << aesItemId << ": " << i; for (int32_t bitFieldIdx = 0; bitFieldIdx < data.impactCount(); ++bitFieldIdx) { // no break, because every bit can be a hit if (LtsUtils::BitArray::testBit(data.bitfield(), bitFieldIdx)) continue; // get position of impact to corresponding bit if (aesItemView.form().size() <= size_t(bitFieldIdx)) continue; const Core::GeoPosition &impactPosition = aesItemView.form()[bitFieldIdx]; DEBUG_LOG(m_logger) << LOGFUNCTION << " impactPosition: lat/lon" << impactPosition.lat() << "/" << impactPosition.lon() << " z=" << impactPosition.z() << " UTM=" << CentralEffectSimulation::debug(CentralEffectSimulation::geographic2Utm(impactPosition)); // check, if impact-pos + munition-radius touch the building // get radius of munition const double impactRadius = m_ddsHandler->chargeTypeRadius(aesItemView.chargeStruct()); if (inFootprint(impactPosition, footprint, impactRadius)) { DEBUG_LOG(m_logger) << "In Footprint of mseId = " << footprint.mseId; if (!bitfieldState.contains(bitFieldIdx)) { DEBUG_LOG(m_logger) << "m_bitfieldStates.insert(aesItemId=" << aesItemId << ", bitfieldState)" << " bitFieldIdx=" << bitFieldIdx; bitfieldState.insert(bitFieldIdx); DEBUG_LOG(m_logger) << "sendCesHit mseId = " << footprint.mseId << ", aesItemId=" << aesItemId << ", bitFieldIdx= " << bitFieldIdx << ", ignFuncBase = " << ignFuncBase; if (! qFuzzyIsNull(impactPosition.z())) { // since HandlerBuildingModel::handleDetonationLocationRequest has already // calculated to fuseType, the impactPosition is the detonationLocation: Core::GeoPosition detonationLocation = impactPosition; // double detonationDistance = 0; switch (ignFuncBase.underlying()) { case ProjectDataManagement::IgniterFunctionBase::IfbProximity: detonationDistance = -5; break; case ProjectDataManagement::IgniterFunctionBase::IfbImpact: detonationDistance = 0; break; case ProjectDataManagement::IgniterFunctionBase::IfbDelay: detonationDistance = 5; break; default: // empty break; } // switch Buildings::SendCesHit3DonBuildingRequest requestBld( footprint.mseId, aesItemView.chargeStruct().chargeTypeId(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).destructiveFactor(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).penetrationFactor(), aesItemId, m_ddsHandler->currentPartitionTime(), impactPosition, detonationLocation, detonationDistance, QStringLiteral("properties").toStdString()); DEBUG_LOG(m_logger) << "sendCesHit3DonBuildingRequest for aesItemId=" << aesItemId << " bitFieldIdx= " << bitFieldIdx << ", requestBld= " << requestBld; m_ddsHandler->ftcBld()->sendCesHit3DonBuildingRequest(requestBld, this); } else { // z-Value is 0 -> old / ! AtExplosionClusterSingleExplosions Buildings::SendCesHitOnBuildingRequest requestBld( footprint.mseId, aesItemView.chargeStruct().chargeTypeId(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).destructiveFactor(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).penetrationFactor(), aesItemId, m_ddsHandler->currentPartitionTime(), Core::GeoPosition(impactPosition.lon(), impactPosition.lat(), 0.0), QStringLiteral("properties").toStdString()); DEBUG_LOG(m_logger) << "sendCesHitOnBuildingRequest for aesItemId=" << aesItemId << " bitFieldIdx= " << bitFieldIdx << ", impactPosition= " << impactPosition; m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this); } // if (impactPosition.z() > 0) } else DEBUG_LOG(m_logger) << "NOT in Footprint of mseId = " << footprint.mseId; // if (inFootprint(impactPosition } // foreach Impact } // for (int32_t bitFieldId } void HandlerAesData::calculateImpacts(const AreaEffectSimulation::Effects &effects, const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint) { if (effects.aesTypeId().underlying() != AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions) return; const int aesItemId = effects.aesItemId(); DEBUG_LOG(m_logger) << "calculateImpacts mseId=" << footprint.mseId << " data.aesItemId() " << aesItemId; const AreaEffectSimulation::ExplosionCluster &ec = m_ddsHandler->dtcAes()->explosionCluster(effects.aesItemId()); if (ec.id() != aesItemId) return; const ProjectDataManagement::IgniterFunctionBase ignFuncBase = igniterFunctionBaseExplosionCluster(aesItemId); const double impactRadius = m_ddsHandler->chargeTypeRadius(ec.chargeStruct()); CentralEffectSimulation::BitfieldStates &bitfieldStates = m_bitfieldStatesBld[footprint.mseId]; QSet &bitfieldState = bitfieldStates[aesItemId]; const std::vector &singleEffectsRef = effects.singleEffects(); int indexBitFieldIdx = -1; for (const AreaEffectSimulation::SingleEffect &singleEffect : qAsConst(singleEffectsRef)) { ++indexBitFieldIdx; // just SesTriggered are relevant: if (singleEffect.singleEffectState() != AreaEffectSimulation::AesSingleEffectState_def::SesTriggered) continue; // already processed? if (bitfieldState.contains(indexBitFieldIdx)) continue; // is building in range? const Core::GeoPosition &impactPositionGeo = singleEffect.position(); if (!inFootprint(impactPositionGeo, footprint, impactRadius)) continue; // Hier könnte man zunächst zwei-dimensional prüfen, ob ein anderes Bldg eine Verdeckung erzeugt zw. // a) Explosionsort (impactPosition) und // b) Grundriss (footprint) des Kandidaten // Diese Verdeckung würde die Wirkung auf den Kandidaten reduzieren. // 2DpoylgonCentroid https://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon const LtsCoordTrans::LtsCoordinate impactPos(impactPositionGeo); const QVector &buildingIdsInBetween = findBuildingsBetween(impactPos, footprint); QString mseIdAsStr; for (auto mseId : buildingIdsInBetween) mseIdAsStr += QString(QStringLiteral("_coveredByMseId_%1;")).arg(mseId); DEBUG_LOG(m_logger) << "findBuildingsBetween returns mseIdAsStr =" << mseIdAsStr; const ProjectDataManagement::BuildingChargeEffect &bldgChargeEffect = m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(ec.chargeStruct().chargeTypeId()); if (bldgChargeEffect.id() <= 0) ERROR_LOG(m_logger) << "getBuildingChargeEffectByChargeTypeId failed, chargeTypeId not found: =" << ec.chargeStruct().chargeTypeId(); qint32 destructiveFactor = bldgChargeEffect.destructiveFactor(); qint32 penetrationFactor = bldgChargeEffect.penetrationFactor(); if ((destructiveFactor <= 0) || (penetrationFactor <= 0)) WARNING_LOG(m_logger) << "getBuildingChargeEffectByChargeTypeId failed, destructiveFactor or penetrationFactor invalid" << ", destructiveFactor = " << destructiveFactor << ", penetrationFactor = " << penetrationFactor; if (!buildingIdsInBetween.empty()) { destructiveFactor /= 2; penetrationFactor /= 2; } QString properties = QStringLiteral("properties"); properties += mseIdAsStr; const AreaEffectSimulation::AesZPlacement_def::type zPlacement = singleEffect.zPlacement().underlying(); if (zPlacement == AreaEffectSimulation::AesZPlacement_def::ZpDraped) { // z-Value is 0 -> old: Buildings::SendCesHitOnBuildingRequest requestBld( footprint.mseId, ec.chargeStruct().chargeTypeId(), destructiveFactor, penetrationFactor, aesItemId, m_ddsHandler->currentPartitionTime(), Core::GeoPosition(impactPositionGeo.lon(), impactPositionGeo.lat(), 0.0), properties.toStdString()); DEBUG_LOG(m_logger) << "sendCesHitOnBuildingRequest for aesItemId=" << aesItemId << " bitFieldIdx= " << indexBitFieldIdx << ", impactPositionGeo= " << impactPositionGeo << ", chargeTypeId= " << ec.chargeStruct().chargeTypeId() << ", destructiveFactor= " << destructiveFactor << ", penetrationFactor= " << penetrationFactor; m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this); bitfieldState.insert(indexBitFieldIdx); } else if (zPlacement == AreaEffectSimulation::AesZPlacement_def::ZpAbsolute) { // since HandlerBuildingModel::handleDetonationLocationRequest has already // calculated to ignFuncBase, the impactPosition is the detonationLocation: Core::GeoPosition detonationLocation = impactPositionGeo; double detonationDistance = 0; switch (ignFuncBase.underlying()) { case ProjectDataManagement::IgniterFunctionBase::IfbProximity: detonationDistance = -5; break; case ProjectDataManagement::IgniterFunctionBase::IfbImpact: detonationDistance = 0; break; case ProjectDataManagement::IgniterFunctionBase::IfbDelay: detonationDistance = 5; break; default: // empty break; } // switch Buildings::SendCesHit3DonBuildingRequest requestBld( footprint.mseId, ec.chargeStruct().chargeTypeId(), destructiveFactor, penetrationFactor, aesItemId, m_ddsHandler->currentPartitionTime(), impactPositionGeo, detonationLocation, detonationDistance, properties.toStdString()); DEBUG_LOG(m_logger) << "SendCesHit3DonBuildingRequest for aesItemId=" << aesItemId << " bitFieldIdx= " << indexBitFieldIdx << ", impactPosition= " << impactPositionGeo << ", zPlacement= " << zPlacement << ", chargeTypeId= " << ec.chargeStruct().chargeTypeId() << ", destructiveFactor= " << destructiveFactor << ", penetrationFactor= " << penetrationFactor; m_ddsHandler->ftcBld()->sendCesHit3DonBuildingRequest(requestBld, this); bitfieldState.insert(indexBitFieldIdx); } // if } // for } void HandlerAesData::checkBuildingsHit(const AreaEffectSimulation::AesItemView &data) { const int exerciseId = m_ddsHandler->getExerciseId(); QPolygonF polyGeo; DEBUG_LOG(m_logger) << "checkBuildingsHit " << ", aesItemId=" << data.aesItemId(); DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary data.form().size()=" << data.form().size(); // BoobyTrap polyGeo.reserve(data.form().size()); for (const auto &pointGeo : qAsConst(data.form())) { QPointF point; point.setX(pointGeo.lon()); point.setY(pointGeo.lat()); DEBUG_LOG(m_logger) << "point= " << point.x() << "/" << point.y(); polyGeo.append(point); } bool completeEnclosed = false; bool isGroundLevel = true; const QVector &buildingIdsInside = m_ddsHandler->dtcIsm()->getBuildingByPolygon(exerciseId, polyGeo, completeEnclosed, isGroundLevel); DEBUG_LOG(m_logger) << "buildingsInside.size=" << buildingIdsInside.size() << " exerciseId=" << exerciseId; // only include buildings within the radius for (const int buildingId : qAsConst(buildingIdsInside)) { DEBUG_LOG(m_logger) << " BuildingId=" << buildingId; Buildings::Building tmpBldg; tmpBldg.buildingId(buildingId); Core::GeoPosition impactPosition; if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary) { impactPosition.lat(m_ddsHandler->dtcAes()->boobyTrap(data.aesItemId()).impactPosition().lat()); impactPosition.lon(m_ddsHandler->dtcAes()->boobyTrap(data.aesItemId()).impactPosition().lon()); } else if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary) { int zone = -1; LtsCoordTrans::UtmCoordinate bottomLeft = LtsCoordTrans::UtmCoordinate::fromGeographic(data.boundingBoxBottomLeft().lon(), data.boundingBoxBottomLeft().lat(), zone); LtsCoordTrans::UtmCoordinate topRight = LtsCoordTrans::UtmCoordinate::fromGeographic(data.boundingBoxTopRight().lon(), data.boundingBoxTopRight().lat(), zone); LtsCoordTrans::UtmCoordinate impactPosPoint(topRight.zone, ((topRight.x + bottomLeft.x) / 2), ((topRight.y + bottomLeft.y) / 2), true); impactPosPoint.zone = topRight.zone; QPointF pointGeo = impactPosPoint.geographic(); impactPosition.lon(pointGeo.x()); impactPosition.lat(pointGeo.y()); DEBUG_LOG(m_logger) << "impactPosition lon/lat=" << impactPosition.lon() << "/" << impactPosition.lat() << " impactPosPoint.zone" << impactPosPoint.zone << " impactPosPoint=" << impactPosPoint.x << "/" << impactPosPoint.y << " topRight=" << topRight.x << "/" << topRight.y << " bottomLeft" << bottomLeft.x << "/" << bottomLeft.y; } Buildings::SendCesHitOnBuildingRequest requestBld( tmpBldg.buildingId(), data.chargeStruct().chargeTypeId(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(data.chargeStruct().chargeTypeId()).destructiveFactor(), m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(data.chargeStruct().chargeTypeId()).penetrationFactor(), data.aesItemId(), m_ddsHandler->currentPartitionTime(), impactPosition, QStringLiteral("properties").toStdString()); DEBUG_LOG(m_logger) << "sendCesHitOnBuildingRequest for aesItemId=" << data.aesItemId() << ", impactPosition= " << impactPosition; m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this); } } QVector HandlerAesData::findBuildingsBetween(const LtsCoordTrans::LtsCoordinate &impactPosition, const CentralEffectSimulation::HandlerBuildingData::Footprint &targetFootprint) { QVector buildingIdsInBetween; // use zone of impactPosition const LtsCoordTrans::UtmCoordinate &impactPositionUTM = impactPosition.utmCoordinate(); const int zone = impactPositionUTM.zone; const LtsCoordTrans::LtsCoordinate targetFootprintPos = LtsCoordTrans::LtsCoordinate::fromUtm(targetFootprint.polyUtm.first(), targetFootprint.zone, 0.0); DEBUG_LOG(m_logger) << "findBuildingsBetween" << " START impactPosition " << impactPositionUTM.mgrs() << ", maybe effected mseId is = " << targetFootprint.mseId << " with targetFootprintPos = " << targetFootprintPos.utmCoordinate().mgrs(); // line between a) impactPosition and b) targetFootprintPos QPolygonF impactToFootprintLineMetric; impactToFootprintLineMetric.reserve(2); // // a) impactPosition // impactToFootprintLineMetric.append(impactPosition.utmCoordinateAsPointF(zone)); // // b) targetFootprintPos // impactToFootprintLineMetric.append(targetFootprintPos.utmCoordinateAsPointF(zone)); // a) impactPosition impactToFootprintLineMetric.append(impactPosition.toUtm()); // b) targetFootprintPos impactToFootprintLineMetric.append(targetFootprintPos.toUtm(zone)); // check all exercise specific footprints except our: const QHash &allFootprintsOfEx = m_handlerBuildingData->getFootprints(); buildingIdsInBetween.reserve(allFootprintsOfEx.size()); for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprintToCheck : qAsConst(allFootprintsOfEx)) { // do not check ourself if (footprintToCheck.mseId == targetFootprint.mseId) continue; // check intersection of // line between a) impactPosition and b) targetFootprintPos // and footprint // QPolygonF is your friend :-) #if QT_VERSION < QT_VERSION_CHECK(5,10,0) const bool intersects = impactToFootprintLineMetric.intersected(footprintToCheck.polyUtm).size() > 0; #else const bool intersects = impactToFootprintLineMetric.intersects(footprintToCheck.polyUtm); #endif DEBUG_LOG(m_logger) << "findBuildingsBetween" << " to mseId " << footprintToCheck.mseId << " with curFpTargetPos " << impactPosition.utmCoordinate().mgrs() << " -> intersects = " << intersects; if (intersects) { DEBUG_LOG(m_logger) << "findBuildingsBetween" << " regarding impactPosition " << impactPositionUTM.mgrs() << " and mseId " << targetFootprint.mseId << " is concealed/covered by footpriont of mseId = " << footprintToCheck.mseId; buildingIdsInBetween.append(footprintToCheck.mseId); } } // for return buildingIdsInBetween; } } // namespace CesBuildings