/** * Handler for player data * $Id$ * $HeadURL$ * $Author$ * Created by hjl, SLSE, 2017-02-20 */ #include "handler_aes_data.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include namespace Ces2D { HandlerAesData::HandlerAesData(const std::shared_ptr &ddsHandler, const std::shared_ptr &buildingDataHandler, QObject *parent) : QObject(parent) , m_ddsHandler(ddsHandler) , m_handlerBuildingData(buildingDataHandler) , m_logger(getLogger("HandlerAesData")) { } // end of ctor() //----------------------------------------------------------------------------- HandlerAesData::~HandlerAesData() { } // end of dtor() //----------------------------------------------------------------------------- bool HandlerAesData::init() { auto dtcAes = m_ddsHandler->dtcAes(); auto dtcIsm = m_ddsHandler->dtcIsm(); auto ftcTab = m_ddsHandler->ftcTab(); dtcIsm->enableMainFootprint(); dtcAes->enableAirSupportImpact(); dtcAes->waitForHistoricalDataExplosionSingleCentral(20000); ftcTab->enableSendCesHitInformation(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged, this, &HandlerAesData::onExplosionSingleChanged); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::airSupportImpactChanged, this, &HandlerAesData::onAirSupportImpactChanged); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged, this, &HandlerAesData::onTriggeredImpactsChanged); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged, this, &HandlerAesData::onAesItemViewChanged); connect(dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainFootprintChanged, this, &HandlerAesData::onMainFootprintChanged); // Get 'static' data for all MainFootprints { auto cb = [this](const InfrastructureManagement::MainFootprint & mfp) { onMainFootprintChanged(mfp, DataModification::eAdded); }; if (!dtcIsm->mainFootprints(cb)) return false; } // Get 'static' data for all AesItemView { auto cb = [this](const AreaEffectSimulation::AesItemView & aiv) { onAesItemViewChanged(aiv, DataModification::eAdded); }; if (!dtcAes->aesItemViews(cb)) return false; } //Get AesNames for all Danger Areas connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::dangerAreaChanged, this, cbGetNames); dtcAes->dangerAreas(cbGetNames); //Get AesNames for all Nuclear Contaminations connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::nContaminationChanged, this, cbGetNames); dtcAes->nContaminations(cbGetNames); //Get AesNames for all Mine Areas connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::mineAreaChanged, this, cbGetNames); dtcAes->mineAreas(cbGetNames); //Get AesNames for all Functionsl Areas connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::functionalAreaChanged, this, cbGetNames); dtcAes->functionalAreas(cbGetNames); return true; } // end of init() //----------------------------------------------------------------------------- // /////////////////////////////////// // A E S (AreaEffectSimulation) // /////////////////////////////////// void HandlerAesData::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType) { if (modType != DataModification::eAdded && modType != DataModification::eModified) return; DEBUG_LOG(m_logger) << "Got AesItemView: " << data.effectStateId(); switch (data.effectStateId().underlying()) { case AreaEffectSimulation::AesEffectState_def::EsUnknown: case AreaEffectSimulation::AesEffectState_def::EsError: case AreaEffectSimulation::AesEffectState_def::EsExpired: case AreaEffectSimulation::AesEffectState_def::EsRemoved: removeStates(data); m_aesItemViews.remove(data.aesItemId()); break; case AreaEffectSimulation::AesEffectState_def::EsPrepared: case AreaEffectSimulation::AesEffectState_def::EsPaused: case AreaEffectSimulation::AesEffectState_def::EsActivated: case AreaEffectSimulation::AesEffectState_def::EsFired: m_aesItemViews.insert(data.aesItemId(), data); break; case AreaEffectSimulation::AesEffectState_def::EsCreated: case AreaEffectSimulation::AesEffectState_def::EsReceived: break; } } 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 (BitfieldStates &bfs : m_bitfieldStatesBld) bfs.remove(data.aesItemId()); } // end removeStates 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 to activated if (m_explosionSingleState.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsPrepared && 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) { const double impactRadius = m_ddsHandler->chargeTypeRadius(data.chargeTypeId()); DEBUG_LOG(m_logger) << "Handle ExplosionSingle (EsActivated)"; // Bounding-Box-Check // QMap m_footprints; for (const Footprint &footprint : qAsConst(m_footprints)) { const QPointF impactPos(CentralEffectSimulation::geographic2Utm(data.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)}); if (polyBldgRadius.containsPoint(impactPos, Qt::OddEvenFill)) { //calculateImpacts(data, footprint.mseId); // check, if impact-pos + munition-radius touch the building // get radius of munition const Core::GeoPosition2D impactPosition(data.impactPosition().lon(), data.impactPosition().lat()); if (inFootprint(impactPosition, impactRadius, footprint)) { DEBUG_LOG(m_logger) << "ftc sendCESHitOnBuilding mseId = " << footprint.mseId << " chargeTypeId = " << data.chargeTypeId() << " id = " << data.id() << " currentPartitionTime = " << m_ddsHandler->currentPartitionTime() << " impactPosition.lon = " << impactPosition.lon() << " impactPosition.lat = " << impactPosition.lat(); Buildings::SendCesHitOnBuildingRequest requestBld( footprint.mseId, data.chargeTypeId(), data.id(), m_ddsHandler->currentPartitionTime(), Core::GeoPosition(impactPosition.lon(), impactPosition.lat(), 0.0)); m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this); } else DEBUG_LOG(m_logger) << "Not sendCESHitOnBuilding mseId = " << footprint.mseId; DEBUG_LOG(m_logger) << "boundingBoxImpact.contains(footprint)"; } } // for } } // onExplosionSingleChanged void HandlerAesData::onAirSupportImpactChanged(const AreaEffectSimulation::AirSupportImpact &data, DataModification modType) { DEBUG_LOG(m_logger) << "Got onAirSupportImpactChanged()"; AreaEffectSimulation::ExplosionSingle esData; esData.id(data.id()); esData.chargeTypeId(data.chargeTypeId()); esData.impactPosition(data.impactPosition()); esData.effectStateId(data.effectStateId()); onExplosionSingleChanged(esData, modType); } // onExplosionSingleChanged 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); } // /////////////////////////////////// // I S M (InfrastructureManagement) // /////////////////////////////////// void HandlerAesData::onMainFootprintChanged(const InfrastructureManagement::MainFootprint &data, DataModification modType) { switch (modType) { case DataModification::eAdded: case DataModification::eModified: DEBUG_LOG(m_logger) << "Got MainFootprint (Building)"; calculateFootprint(data); break; case DataModification::eRemoved: m_footprints.remove(data.id()); break; } } // /////////////////////////////////// // Calculations: // /////////////////////////////////// void HandlerAesData::calculateFootprint(const InfrastructureManagement::MainFootprint &mainFootprint) { DEBUG_LOG(m_logger) << "Footprint: " << mainFootprint.id() << ": " << mainFootprint; DEBUG_LOG(m_logger) << "Footprint: " << " mainFootprint.elements().size()=" << mainFootprint.elements().size(); // If poly is Empty continue with next Footprint if (mainFootprint.elements().size() <= 0) return; const InfrastructureManagement::MainStructureElement mse = m_handlerBuildingData->mainStructureElement(mainFootprint.id()); if (mse.id() != mainFootprint.id()) { // not (yet) there QTimer::singleShot(1000, this, [this, mainFootprint]() { calculateFootprint(mainFootprint); }); return; } // insert all Points to poly QPolygonF poly; poly.reserve(mainFootprint.elements().size()); for (const InfrastructureManagement::FootprintElement &element : mainFootprint.elements()) { const auto &relPos = element.relPosition(); poly.push_back(QPointF(relPos.offsetX(), relPos.offsetY())); } // rotate poly QTransform trans; trans.rotate(mse.angle()); poly = trans.map(poly); Footprint footprint; footprint.mseId = mainFootprint.id(); footprint.geoPosition = mse.geoPosition(); const QPointF utmFpPosition = CentralEffectSimulation::geographic2Utm(footprint.geoPosition, &footprint.zone); footprint.polyUtm.reserve(poly.size()); for (const QPointF &point : qAsConst(poly)) footprint.polyUtm.push_back(utmFpPosition + point); footprint.boundingBoxPolyUtm = footprint.polyUtm.boundingRect(); m_footprints.insert(footprint.mseId, footprint); } bool HandlerAesData::inFootprint(const Core::GeoPosition2D &geoPosition, const double radius, const Footprint &footPrint) const { const QPointF impact(CentralEffectSimulation::geographic2Utm(geoPosition, footPrint.zone)); 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 // Search corresponding itemView and get boundingBox data in UTM const bool bFound = m_aesItemViews.contains(data.aesItemId()); if (!bFound) { WARNING_LOG(m_logger) << "Could not find item view for aes with id " << data.aesItemId(); return; } // Bounding-Box-Check // QMap m_footprints; for (const Footprint &footprint : qAsConst(m_footprints)) { //if (boundingBoxImpact.contains(footprint.boundingBoxPolyUtm)) calculateImpacts(data, 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(); else DEBUG_LOG(m_logger) << "onSendCesHitOnBuildingReply 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"; } // calculate impacts for one building void HandlerAesData::calculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data, const Footprint &footprint) { // check for correct AES type artillery only switch (data.aesTypeId().underlying()) { case AreaEffectSimulation::AesType_def::AtExplosionArea: case AreaEffectSimulation::AesType_def::AtExplosionSingle: case AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions: DEBUG_LOG(m_logger) << "Calculate"; break; default: DEBUG_LOG(m_logger) << "No Calculation"; return; } DEBUG_LOG(m_logger) << "calculateImpacts mseId=" << footprint.mseId << " data.aesItemId() " << data.aesItemId(); // get position(s) of impact(s): // Search corresponding itemView. If not available no impact to participant. auto it = m_aesItemViews.constFind(data.aesItemId()); if (it == m_aesItemViews.constEnd()) { WARNING_LOG(m_logger) << "TriggeredExplosions.aesItemId=" << data.aesItemId() << " No aesItemView available"; return; } BitfieldStates &bitfieldStates = m_bitfieldStatesBld[footprint.mseId]; QSet &bitfieldState = bitfieldStates[data.aesItemId()]; for (int i : qAsConst(bitfieldState)) DEBUG_LOG(m_logger) << "bitfieldState for " << data.aesItemId() << ": " << i; const AreaEffectSimulation::AesItemView &aesItemView = *it; 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::GeoPosition2D &impactPosition = aesItemView.form()[bitFieldIdx]; DEBUG_LOG(m_logger) << LOGFUNCTION << " impactPosition: lat/lon" << impactPosition.lat() << "/" << impactPosition.lon() << " 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.chargeTypeId()); if (inFootprint(impactPosition, impactRadius, footprint)) { DEBUG_LOG(m_logger) << "In Footprint mseId = " << footprint.mseId; if (!bitfieldState.contains(bitFieldIdx)) { bitfieldState.insert(bitFieldIdx); DEBUG_LOG(m_logger) << "m_bitfieldStates.insert(data.aesItemId()=" << data.aesItemId() << ", bitfieldState)" << " bitFieldIdx=" << bitFieldIdx; DEBUG_LOG(m_logger) << "ftc sendCESHitOnBuilding mseId = " << footprint.mseId << " bitFieldIdx= " << bitFieldIdx ; Buildings::SendCesHitOnBuildingRequest requestBld( footprint.mseId, aesItemView.chargeTypeId(), aesItemView.aesItemId(), m_ddsHandler->currentPartitionTime(), Core::GeoPosition(impactPosition.lon(), impactPosition.lat(), 0.0)); m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this); } } } // foreach Impact } } // namespace Ces2D