/** * Handler for player data * $Id$ * $HeadURL$ * $Author$ * Created by gr, SLSE, 2017-02-20 */ #include "handler_participant_area.h" #include "settings.h" #include "CentralEffectSimulation/handler_participant.h" #include #include #include #include #include #include namespace dds { namespace core { inline uint qHash(ProjectDataManagement::EventType et) { return ::qHash(et.underlying()); } } } namespace Ces2D { inline uint qHash(EffectType et) { return ::qHash(int(et)); } QString toString(EffectType et) { switch (et) { case EffectType::Unknown: return QStringLiteral("Unknown"); case EffectType::EffectiveMineRangeEnter: return QStringLiteral("EffectiveMineRangeEnter"); case EffectType::EffectiveMineRangeLeave: return QStringLiteral("EffectiveMineRangeLeave"); case EffectType::NContaminationLeave: return QStringLiteral("NContaminationLeave"); case EffectType::NContaminationEnterArea1: return QStringLiteral("NContaminationEnterArea1"); case EffectType::NContaminationEnterArea2: return QStringLiteral("NContaminationEnterArea2"); case EffectType::NContaminationDetonation: return QStringLiteral("NContaminationDetonation"); case EffectType::FunctionalAreaLeave: return QStringLiteral("FunctionalAreaLeave"); case EffectType::FunctionalAreaEnter: return QStringLiteral("FunctionalAreaEnter"); case EffectType::ExplosionArea: return QStringLiteral("ExplosionArea"); case EffectType::ExplosionSingle: return QStringLiteral("ExplosionSingle"); case EffectType::BCContaminationLeave: return QStringLiteral("BCContaminationLeave"); case EffectType::BCContaminationEnterFloating: return QStringLiteral("BCContaminationEnterFloating"); case EffectType::BCContaminationEnterResident: return QStringLiteral("BCContaminationEnterResident"); case EffectType::BCContaminationEnterFloatingArea: return QStringLiteral("BCContaminationEnterFloatingArea"); case EffectType::BeStillWarning: return QStringLiteral("BeStillWarning"); } return QStringLiteral("Unknown (%1)").arg(int(et)); } HandlerParticipantArea::HandlerParticipantArea(const std::shared_ptr &ddsHandler, const std::shared_ptr &participantHandler, const std::shared_ptr &aesDataHandler, const std::shared_ptr &buildingModelHandler, QObject *parent) : QObject(parent) , m_ddsHandler(ddsHandler) , m_handlerParticipant(participantHandler) , m_handlerAesData(aesDataHandler) , m_handlerBuildingModel(buildingModelHandler) , m_logger(getLogger("HandlerParticipantArea")) { } // end of ctor() //----------------------------------------------------------------------------- HandlerParticipantArea::~HandlerParticipantArea() { } // end of dtor() //----------------------------------------------------------------------------- bool HandlerParticipantArea::init() { auto dtcAes = m_ddsHandler->dtcAes(); DEBUG_LOG(m_logger) << "Settings::calculateCentralAes() =" << Settings::calculateCentralAes(); dtcAes->enableBCContamination(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::bCContaminationChanged, this, &HandlerParticipantArea::onBCContaminationChanged); dtcAes->enableNContamination(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::nContaminationChanged, this, &HandlerParticipantArea::onNContaminationChanged); dtcAes->enableTriggeredImpacts(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged, this, &HandlerParticipantArea::onTriggeredImpactsChanged); dtcAes->enableExplosionArea(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged, this, &HandlerParticipantArea::onExplosionAreaChanged); dtcAes->enableExplosionSingle(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged, this, &HandlerParticipantArea::onExplosionSingleChanged); dtcAes->enableExplosionSingleCentral(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleCentralChanged, this, &HandlerParticipantArea::onExplosionSingleCentralChanged); dtcAes->enableAesItemView(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged, this, &HandlerParticipantArea::onAesItemViewChanged); dtcAes->enableDusSingleMine(); connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::dusSingleMineChanged, this, &HandlerParticipantArea::onDusSingleMineChanged); connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newIndoorPosition, this, &HandlerParticipantArea::calculateAesParticipant); connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newOutdoorPosition, this, &HandlerParticipantArea::calculateAesParticipant); // Get 'static' data for all bcContaminations { auto cb = [this](const AreaEffectSimulation::BCContamination & bc) { onBCContaminationChanged(bc, DataModification::eAdded); }; if (!dtcAes->bCContaminations(cb)) return false; } // Get 'static' data for all explosionAreas { auto cb = [this](const AreaEffectSimulation::NContamination & nc) { onNContaminationChanged(nc, DataModification::eAdded); }; if (!dtcAes->nContaminations(cb)) return false; } // Get 'static' data for all explosionSingle { auto cb = [this](const AreaEffectSimulation::ExplosionSingle & es) { onExplosionSingleChanged(es, DataModification::eAdded); }; if (!dtcAes->explosionSingles(cb)) return false; } // Get 'static' data for all explosionSingleCentral { auto cb = [this](const AreaEffectSimulation::ExplosionSingleCentral & esc) { onExplosionSingleCentralChanged(esc, DataModification::eAdded); }; if (!dtcAes->explosionSingleCentrals(cb)) return false; } // Get 'static' data for all onAesItemView { auto cb = [this](const AreaEffectSimulation::AesItemView & aiv) { onAesItemViewChanged(aiv, DataModification::eAdded); }; if (!dtcAes->aesItemViews(cb)) return false; } // Get 'static' data for all DusSingleMines { auto cb = [this](const AreaEffectSimulation::DusSingleMine & fa) { onDusSingleMineChanged(fa, DataModification::eAdded); }; if (!dtcAes->dusSingleMines(cb)) return false; } // Get 'static' data for all explosionAreas { auto cb = [this](const AreaEffectSimulation::ExplosionArea & ea) { onExplosionAreaChanged(ea, DataModification::eAdded); }; if (!dtcAes->explosionAreas(cb)) return false; } INFO_LOG(m_logger) << "HandlerParticipantArea::init() finished."; return true; } // end of init() //----------------------------------------------------------------------------- void HandlerParticipantArea::onSendCesHitReply(const Core::FunctionReplyObject &replyObject) { DEBUG_LOG(m_logger) << "onSendCesHitReply for id " << replyObject.id() << " success= " << replyObject.error(); if (replyObject.error()) WARNING_LOG(m_logger) << "Can not send enter/leave FunctionsArea to participant in training area: " << replyObject(); // Send next Request, if available if (!m_sendCesHitRequestQueue.isEmpty()) { DEBUG_LOG(m_logger) << "m_sendCesAreaActionRequestQueue.size()=" << m_sendCesHitRequestQueue.size(); m_sendCesHitRequestQueue.dequeue(); // Send next Message if (!m_sendCesHitRequestQueue.isEmpty()) { const auto &hr = m_sendCesHitRequestQueue.head(); DEBUG_LOG(m_logger) << "sendCesHitRequest: " << hr; m_ddsHandler->ftcTab()->sendCesHitRequest(hr, this); } } } // /////////////////////////////////// // A E S (AreaEffectSimulation) // /////////////////////////////////// void HandlerParticipantArea::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType) { // not needed, handled through HandlerParticipantGasPipe if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtGasPipe) return; if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtUnknown) return; if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrap) return; if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMine) return; if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary) return; if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtMineArea) return; if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtMineCorridor) return; if (m_ddsHandler->isSmartChargeType(data.chargeTypeId())) { DEBUG_LOG(m_logger) << "isSmartType = TRUE"; return; } const int aesItemId = data.aesItemId(); DEBUG_LOG(m_logger) << "Got AesItemView id: " << aesItemId << ", type: " << data.aesTypeId() << ", effectState: " << data.effectStateId() << ", modType: " << modType; if (modType == DataModification::eRemoved) { aesItemViewRemoved(aesItemId); return; } if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtMineAreaSingleMines) { // build Polygon Mines: auto &formMines = m_aesItemViews[data.aesItemId()].formMines; int &zone = m_aesItemViews[data.aesItemId()].zone; formMines.clear(); formMines.reserve(data.form().size()); for (const Core::GeoPosition2D &pointGeo : data.form()) formMines.push_back(CentralEffectSimulation::geographic2Utm(pointGeo, &zone)); return; } const AreaEffectSimulation::AesEffectState &effectState = data.effectStateId(); const double impactRadius = m_ddsHandler->chargeTypeRadius(data.chargeTypeId()); DEBUG_LOG(m_logger) << "impactRadius=" << impactRadius; switch (effectState.underlying()) { case AreaEffectSimulation::AesEffectState_def::EsPrepared: { m_aesItemViews.insert(data.aesItemId(), CentralEffectSimulation::AesItemViewUtm::create(data, m_aesItemViews[data.aesItemId()], impactRadius)); break; } case AreaEffectSimulation::AesEffectState_def::EsActivated: { const auto aesItemViewUtm(CentralEffectSimulation::AesItemViewUtm::create(data, m_aesItemViews[data.aesItemId()], impactRadius)); switch (data.aesTypeId().underlying()) { case AreaEffectSimulation::AesType_def::AtExplosionArea: case AreaEffectSimulation::AesType_def::AtExplosionSingle: case AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions: case AreaEffectSimulation::AesType_def::AtMineArea: case AreaEffectSimulation::AesType_def::AtAirSupportImpact: m_aesItemViews.insert(data.aesItemId(), aesItemViewUtm); break; case AreaEffectSimulation::AesType_def::AtBCContamination: case AreaEffectSimulation::AesType_def::AtNContamination: // for each participant: // Bounding-Box Check, // form-Check // if new field for this participant send sendCesHitRequest calculateBcContamination(aesItemViewUtm); m_aesItemViews.insert(data.aesItemId(), aesItemViewUtm); break; case AreaEffectSimulation::AesType_def::AtFunctionalArea: case AreaEffectSimulation::AesType_def::AtDangerArea: calculateFunctionArea(aesItemViewUtm); m_aesItemViews.insert(data.aesItemId(), aesItemViewUtm); break; case AreaEffectSimulation::AesType_def::AtUnknown: case AreaEffectSimulation::AesType_def::AtMineCorridor: case AreaEffectSimulation::AesType_def::AtBorderline: case AreaEffectSimulation::AesType_def::AtReconArea: case AreaEffectSimulation::AesType_def::AtTechnicalArea: case AreaEffectSimulation::AesType_def::AtDusSingleMine: case AreaEffectSimulation::AesType_def::AtMineAreaSingleMines: case AreaEffectSimulation::AesType_def::AtGasPipe: case AreaEffectSimulation::AesType_def::AtExplosionSingleCentral: case AreaEffectSimulation::AesType_def::AtExplosionSingleCentralTrajectory: case AreaEffectSimulation::AesType_def::AtBoobyTrap: case AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary: case AreaEffectSimulation::AesType_def::AtOffRouteMine: case AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary: break; } // switch aesTypeId break; } // case EsActivated case AreaEffectSimulation::AesEffectState_def::EsRemoved: case AreaEffectSimulation::AesEffectState_def::EsExpired: { aesItemViewRemoved(aesItemId); break; } default: // do nothing break; } // switch effectStateId } // onAesItemViewChanged void HandlerParticipantArea::onDusSingleMineChanged(const AreaEffectSimulation::DusSingleMine &data, DataModification modType) { switch (modType) { case DataModification::eRemoved: break; case DataModification::eAdded: case DataModification::eModified: switch (data.effectStateId().underlying()) { case AreaEffectSimulation::AesEffectState_def::EsActivated: { //DEBUG_LOG(m_logger) << "Got onDusSingleMineChanged data.id()=" << data.id(); CentralEffectSimulation::AesItemViewUtm itemView; itemView.aesItemId = data.id(); itemView.chargeTypeId = data.chargeTypeId(); itemView.partyId = data.partyId().is_set() ? data.partyId().get() : -1; itemView.aesTypeId = AreaEffectSimulation::AesType_def::AtDusSingleMine; itemView.effectStart = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); itemView.formMines.push_back(CentralEffectSimulation::geographic2Utm(data.position(), &itemView.zone)); itemView.dusMineId = data.mineId(); m_aesItemViews[itemView.aesItemId] = itemView; AreaEffectSimulation::TriggeredImpacts ti; ti.aesItemId(itemView.aesItemId); ti.bitfield().resize(1, 0xff); m_triggeredImpacts[itemView.aesItemId] = ti; break; } case AreaEffectSimulation::AesEffectState_def::EsPrepared: case AreaEffectSimulation::AesEffectState_def::EsExpired: case AreaEffectSimulation::AesEffectState_def::EsRemoved: m_aesItemViews.remove(data.id()); break; default: // Nothing break; } break; } } void HandlerParticipantArea::onBCContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType) { switch (modType) { case DataModification::eRemoved: break; case DataModification::eAdded: case DataModification::eModified: switch (data.effectStateId().underlying()) { case AreaEffectSimulation::AesEffectState_def::EsReceived: case AreaEffectSimulation::AesEffectState_def::EsCreated: case AreaEffectSimulation::AesEffectState_def::EsPrepared: case AreaEffectSimulation::AesEffectState_def::EsActivated: { CentralEffectSimulation::AesItemViewUtm &ref = m_aesItemViews[data.id()]; const auto dtcPdm = m_ddsHandler->dtcPdm(); if (dtcPdm->volatilityTypeId(data.chargeTypeId()) == ProjectDataManagement::VolatilityType_def::Resident) { ref.radials = {data.impactRadius()}; ref.center = CentralEffectSimulation::geographic2Utm(data.impactPosition(), &ref.zone); } else ref.radials.clear(); break; } default: break; } } } void HandlerParticipantArea::onNContaminationChanged(const AreaEffectSimulation::NContamination &data, DataModification modType) { switch (modType) { case DataModification::eRemoved: break; case DataModification::eAdded: case DataModification::eModified: switch (data.effectStateId().underlying()) { case AreaEffectSimulation::AesEffectState_def::EsReceived: case AreaEffectSimulation::AesEffectState_def::EsCreated: case AreaEffectSimulation::AesEffectState_def::EsPrepared: case AreaEffectSimulation::AesEffectState_def::EsActivated: { CentralEffectSimulation::AesItemViewUtm &ref = m_aesItemViews[data.id()]; ref.radials = {data.impactRadius(), data.borderLengthInner(), data.borderLengthInner() + data.borderLengthOuter() }; ref.center = CentralEffectSimulation::geographic2Utm(data.impactPosition(), &ref.zone); break; } default: break; } } } void HandlerParticipantArea::calculateBcContamination(const CentralEffectSimulation::AesItemViewUtm &itemView) { DEBUG_LOG(m_logger) << "HandlerParticipantArea::calculateBcContamination aesItemId =" << itemView.aesItemId; const auto participantsMap = m_handlerParticipant->positions(); for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos : participantsMap) { if (m_handlerParticipant->isOutdated(participantPos)) // Participant Position is not valid Todo Magic Number continue; inFormCheck(itemView, participantPos); } // for } // calculateBcContamination void HandlerParticipantArea::calculateFunctionArea(const CentralEffectSimulation::AesItemViewUtm &itemView) { DEBUG_LOG(m_logger) << "HandlerParticipantArea::calculateFunctionArea aesItemId =" << itemView.aesItemId; const auto partPosMap = m_handlerParticipant->positions(); for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos : partPosMap) { if (m_handlerParticipant->isOutdated(participantPos)) continue; inFormCheck(itemView, participantPos); } } // calculateFunctionArea void HandlerParticipantArea::deleteFunctionalArea(const int aesItemId) { CentralEffectSimulation::AesItemViewUtm itemView = m_aesItemViews.value(aesItemId); DEBUG_LOG(m_logger) << "delete " << itemView.aesItemId << ", aesItemId: " << aesItemId; const auto partPosMap = m_handlerParticipant->positions(); for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos : partPosMap) { if (m_handlerParticipant->isOutdated(participantPos)) continue; // Same as leave FunctionArea if (itemView.aesTypeId == AreaEffectSimulation::AesType_def::AtFunctionalArea) { sendParticipantEvent(itemView, participantPos, ProjectDataManagement::EventType_def::LeaveFunctionalArea); sendEffectTypeEvent(itemView, participantPos, EffectType::FunctionalAreaLeave, -1, -1); } else sendParticipantEvent(itemView, participantPos, ProjectDataManagement::EventType_def::LeaveDangerArea); DEBUG_LOG(m_logger) << "Participant inside id=" << participantPos.participantId(); } // for all Participants } // deleteFunctionalArea void HandlerParticipantArea::calculateAesParticipant(const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos) { if (m_handlerParticipant->isOutdated(participantPos)) return; DEBUG_LOG(m_logger) << "HandlerParticipantArea::calculateAesParticipant paricipantId: " << participantPos.participantId(); for (const CentralEffectSimulation::AesItemViewUtm &itemView : qAsConst(m_aesItemViews)) { DEBUG_LOG(m_logger) << " itemView: " << itemView.aesItemId << ", " << itemView.aesTypeId; switch (itemView.aesTypeId.underlying()) { case AreaEffectSimulation::AesType_def::AtMineArea: if (inFormCheck(itemView, participantPos)) // Check, if Participant is in a circle of mine inMineRadiusCheck(itemView, participantPos); else // no longer in mine area - reset all EffectiveMineRangeEnter mineIndexes m_effectTypeBitFieldIndexes[itemView.aesItemId][participantPos.participantId()].clear(); break; case AreaEffectSimulation::AesType_def::AtMineAreaSingleMines: break; case AreaEffectSimulation::AesType_def::AtDusSingleMine: inMineRadiusCheck(itemView, participantPos); break; case AreaEffectSimulation::AesType_def::AtBCContamination: case AreaEffectSimulation::AesType_def::AtNContamination: case AreaEffectSimulation::AesType_def::AtFunctionalArea: case AreaEffectSimulation::AesType_def::AtDangerArea: inFormCheck(itemView, participantPos); break; default: // nothing break; } // switch } // for } // calculateAesParticipant // return true, if participant is inside of the dangerous area of a mine bool HandlerParticipantArea::inMineRadiusCheck(const CentralEffectSimulation::AesItemViewUtm &itemView, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition) { int32_t bitFieldIdx = 0; const double impactRadius = m_ddsHandler->chargeTypeRadius(itemView.chargeTypeId); const AreaEffectSimulation::TriggeredImpacts &triggeredImpact = m_triggeredImpacts[itemView.aesItemId]; const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone); bool bOutOfBounds = false; for (const auto &itPos : itemView.formMines) { DEBUG_LOG(m_logger) << "participantPositionUtm: " << CentralEffectSimulation::debug(participantPosUtmPoint) << ", MinePosUtm: " << CentralEffectSimulation::debug(itPos); // Check, if mine is active // already triggered (bit is already 0) if (!LtsUtils::BitArray::testBit(triggeredImpact.bitfield(), bitFieldIdx) && !bOutOfBounds) { ++bitFieldIdx; continue; } const int mineIdx = (itemView.aesTypeId == AreaEffectSimulation::AesType_def::AtDusSingleMine) ? itemView.dusMineId : bitFieldIdx + 1; // mineIdx in minefield starts at 1 // Check, if participant is inside danger-radius of the mine if (inCircle(itPos, impactRadius, participantPosUtmPoint)) { DEBUG_LOG(m_logger) << "Mine Hit for aesItemId: " << itemView.aesItemId << ", mineIdx: " << mineIdx << ", participantId: " << participantPosition.participantId(); sendEffectTypeEvent(itemView, participantPosition, EffectType::EffectiveMineRangeEnter, mineIdx, -1); } else sendEffectTypeEvent(itemView, participantPosition, EffectType::EffectiveMineRangeLeave, mineIdx, -1); bitFieldIdx++; } return false; } // return true, if participant is inside of the bounding Box bool HandlerParticipantArea::inBoundingBoxCheck(const CentralEffectSimulation::AesItemViewUtm &itemView, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition) { if (itemView.effectStateId != AreaEffectSimulation::AesEffectState_def::EsActivated) return false; if (m_handlerParticipant->isOutdated(participantPosition)) // Participant Position is not valid Todo Magic Number return false; // QRectF(topLeft, bottomRight) -> no need to mormalize, QRectF:contains() handles it correct const QRectF boundingBoxImpact(itemView.boundingBoxBottomLeft, itemView.boundingBoxTopRight); const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone); if (boundingBoxImpact.contains(participantPosUtmPoint)) { DEBUG_LOG(m_logger) << "inBoundingBoxCheck Paricipant " << participantPosition.participantId() << " inside AesItemView:" << itemView.aesItemId; return true; } TRACE_LOG(m_logger) << "inBoundingBoxCheck Paricipant " << participantPosition.participantId() << " outside AesItemView:" << itemView.aesItemId; return false; } // return true, if participant is inside of the form of AesItemView bool HandlerParticipantArea::inFormCheck(const CentralEffectSimulation::AesItemViewUtm &itemView, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition) { if (itemView.effectStateId != AreaEffectSimulation::AesEffectState_def::EsActivated) return false; if (m_handlerParticipant->isOutdated(participantPosition)) // Participant Position is not valid return false; const QRectF boundingBoxImpact(itemView.boundingBoxBottomLeft, itemView.boundingBoxTopRight); const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone); const QString participantPosMgrs = CentralEffectSimulation::geographic2mgrs(participantPosition.pos(), itemView.zone); DEBUG_LOG(m_logger) << "inFormCheck start, paricipantId: " << participantPosition.participantId() << ", aesItemId: " << itemView.aesItemId << ", aesTypeId: " << itemView.aesTypeId << ", form.size: " << itemView.form.size() << ", Mgrs:" << participantPosMgrs; if (boundingBoxImpact.contains(participantPosUtmPoint) && itemView.form.containsPoint(participantPosUtmPoint, Qt::FillRule::OddEvenFill)) { DEBUG_LOG(m_logger) << "inside AES form, paricipantId: " << participantPosition.participantId() << ", aesItemId: " << itemView.aesItemId; switch (itemView.aesTypeId.underlying()) { case AreaEffectSimulation::AesType_def::AtBCContamination: sendEffectTypeEvent(itemView, participantPosition, effectTypeForBCContaminationArea(itemView, participantPosUtmPoint), -1, -1); break; case AreaEffectSimulation::AesType_def::AtDangerArea: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterDangerArea); break; case AreaEffectSimulation::AesType_def::AtExplosionArea: break; case AreaEffectSimulation::AesType_def::AtFunctionalArea: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterFunctionalArea); sendEffectTypeEvent(itemView, participantPosition, EffectType::FunctionalAreaEnter, -1, -1); break; case AreaEffectSimulation::AesType_def::AtMineArea: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterMineArea); break; case AreaEffectSimulation::AesType_def::AtNContamination: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterNuclear); sendEffectTypeEvent(itemView, participantPosition, effectTypeForNContaminationArea(itemView, participantPosUtmPoint, participantPosition.sampleTime()), -1, -1); break; default: INFO_LOG(m_logger) << "inFormCheck Unhandled AES-Type" << itemView.aesTypeId; break; } return true; } // if inside // outside switch (itemView.aesTypeId.underlying()) { case AreaEffectSimulation::AesType_def::AtBCContamination: sendEffectTypeEvent(itemView, participantPosition, EffectType::BCContaminationLeave, -1, -1); break; case AreaEffectSimulation::AesType_def::AtDangerArea: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveDangerArea); break; case AreaEffectSimulation::AesType_def::AtExplosionArea: break; case AreaEffectSimulation::AesType_def::AtFunctionalArea: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveFunctionalArea); sendEffectTypeEvent(itemView, participantPosition, EffectType::FunctionalAreaLeave, -1, -1); break; case AreaEffectSimulation::AesType_def::AtMineArea: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveMineArea); break; case AreaEffectSimulation::AesType_def::AtNContamination: sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveNuclear); sendEffectTypeEvent(itemView, participantPosition, EffectType::NContaminationLeave, -1, -1); break; default: break; } // end switch #if 0 if (m_logger.isDebugEnabled()) { DEBUG_LOG(m_logger) << "inFormCheck: Paricipant " << participantPosition.participantId << " outside AesItemView " << itemView.aesItemId; DEBUG_LOG(m_logger) << "ParticipantPos = " << debug(participantPosUtmPoint); for (const auto &itPos : itemView.form) DEBUG_LOG(m_logger) << "itemView =" << debug(itPos); } #endif return false; } void HandlerParticipantArea::onTriggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType) { if (modType != DataModification::eAdded && modType != DataModification::eModified) return; DEBUG_LOG(m_logger) << "Got TriggeredImpacts: " << data; switch (data.aesTypeId().underlying()) { case AreaEffectSimulation::AesType_def::AtExplosionArea: case AreaEffectSimulation::AesType_def::AtExplosionSingle: case AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions: // explosion at the position of participant calculateImpacts(data); break; case AreaEffectSimulation::AesType_def::AtMineAreaSingleMines: case AreaEffectSimulation::AesType_def::AtMineArea: // participant run on mine // => store Mine-State and take into account when a participant runs to one of this mines m_triggeredImpacts.insert(data.aesItemId(), data); break; default: //nothigng break; } } // onTriggeredImpactsChanged void HandlerParticipantArea::onExplosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType) { if (modType != DataModification::eAdded && modType != DataModification::eModified) return; DEBUG_LOG(m_logger) << "Got ExplosionArea, m_aesItemId2partIdMap.insert " << data.id() << " / " << data.participantId(); if (data.participantId().is_set()) m_aesItemId2partIdMap.insert(data.id(), data.participantId().get()); else m_aesItemId2partIdMap.remove(data.id()); } // onExplosionAreaChanged void HandlerParticipantArea::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType) { bool handleExplosion = false; if (m_ddsHandler->isSmartChargeType(data.chargeTypeId())) { DEBUG_LOG(m_logger) << "isSmartType = TRUE"; return; } if (modType != DataModification::eAdded && modType != DataModification::eModified) return; if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved) { DEBUG_LOG(m_logger) << "Remove SingleExplosion"; m_explosionSingleStates.remove(data.id()); return; } // Only handle transitions from prepared or fired to activated if ((m_explosionSingleStates.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsPrepared || m_explosionSingleStates.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsFired) && data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated) { DEBUG_LOG(m_logger) << "Handle SingleExplosion"; handleExplosion = true; } m_explosionSingleStates.insert(data.id(), data.effectStateId()); if (!handleExplosion) return; DEBUG_LOG(m_logger) << "isSmartType = " << m_ddsHandler->isSmartChargeType(data.chargeTypeId()); if (m_ddsHandler->isSmartChargeType(data.chargeTypeId())) { DEBUG_LOG(m_logger) << "isSmartType = TRUE"; } else { int impactPosZone = -1; const QPointF impactPos(CentralEffectSimulation::geographic2Utm(data.impactPosition(), &impactPosZone)); const CentralEffectSimulation::AesItemViewUtm itemView = m_aesItemViews.value(data.id()); const double impactRadius = m_ddsHandler->chargeTypeRadius(data.chargeTypeId()); DEBUG_LOG(m_logger) << "Handle ExplosionSingle (EsActivated)"; const auto participantPositions = m_handlerParticipant->positions(); for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition : participantPositions) { if (participantPosition.insideBuilding()) continue; if (m_handlerParticipant->isOutdated(participantPosition)) continue; const QPointF participantPosUtm = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), impactPosZone); DEBUG_LOG(m_logger) << "impact pos: " << CentralEffectSimulation::debug(impactPos) << " with radius " << impactRadius << " and participant position " << CentralEffectSimulation::debug(participantPosUtm); if (inCircle(impactPos, impactRadius, participantPosUtm)) { DEBUG_LOG(m_logger) << "participantId = " << participantPosition.participantId() << " inCircle = true"; sendEffectTypeEvent(itemView, participantPosition, EffectType::ExplosionSingle, -1, QDateTime::currentMSecsSinceEpoch()); } else DEBUG_LOG(m_logger) << "participantId = " << participantPosition.participantId() << " inCircle = false"; } } } // onExplosionSingleChanged void HandlerParticipantArea::onExplosionSingleCentralChanged(const AreaEffectSimulation::ExplosionSingleCentral &data, DataModification modType) { if (modType != DataModification::eAdded && modType != DataModification::eModified) return; if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved) // EsExpired? EsError? EsUnknown? { DEBUG_LOG(m_logger) << "Remove ExplosionSingleCentral with aesId = " << data.id(); m_explosionSingleCentralStates.remove(data.id()); return; } // if if ((m_explosionSingleCentralStates.value(data.id()) != AreaEffectSimulation::AesEffectState_def::EsPrepared && m_explosionSingleCentralStates.value(data.id()) != AreaEffectSimulation::AesEffectState_def::EsFired) || data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsActivated) { m_explosionSingleCentralStates.insert(data.id(), data.effectStateId()); return; } // if m_explosionSingleCentralStates.insert(data.id(), data.effectStateId()); DEBUG_LOG(m_logger) << "Handle ExplosionSingleCentral with aesId = " << data.id(); // pre-calculate geometric data const AreaEffectSimulation::AesItemView &itemView = m_handlerAesData->itemView(data.id()); const Core::GeoPosition ¢erGeo = itemView.form().front(); const LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(centerGeo.lon(), centerGeo.lat()); const LtsUtils::QPoint3D center(centerUtm.x, centerUtm.y, centerGeo.z()); const Core::GeoPosition &pointOnSphereGeo = itemView.form().back(); const LtsCoordTrans::UtmCoordinate pointOnSphereUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(pointOnSphereGeo.lon(), pointOnSphereGeo.lat()); const LtsUtils::QPoint3D pointOnSphere(pointOnSphereUtm.x, pointOnSphereUtm.y, pointOnSphereGeo.z()); const double radiusSquared = (center - pointOnSphere).lengthSquared(); const int effectType = static_cast(EffectType::ExplosionSingle) & 0xffff; const QMap buildingModels = m_handlerBuildingModel->buildingModels(); const QList participantPositions = m_handlerParticipant->positions().values(); for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition : qAsConst(participantPositions)) { if (participantPosition.insideBuilding()) continue; if (m_handlerParticipant->isOutdated(participantPosition)) continue; const LtsCoordTrans::UtmCoordinate positionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(participantPosition.lastGeoPosition.lon(), participantPosition.lastGeoPosition.lat()); const LtsUtils::QPoint3D position(positionUtm.x, positionUtm.y, participantPosition.lastGeoPosition.z()); const double distanceSquared = (center - position).lengthSquared(); if (distanceSquared > radiusSquared) continue; bool isCovered = false; for (auto it = buildingModels.constBegin(); it != buildingModels.constEnd(); ++it) { const BuildingModel *buildingModel = it.value(); if (buildingModel->hasIntersection(centerGeo, participantPosition.lastGeoPosition)) { DEBUG_LOG(m_logger) << "Participant " << participantPosition.participantId() << " is covered by building " << it.key(); isCovered = true; break; } // if } // for if (isCovered) continue; DEBUG_LOG(m_logger) << "Participant " << participantPosition.participantId() << " is hit"; sendCesHitRequest(itemView, participantPosition, QDateTime::currentMSecsSinceEpoch(), effectType, -1); } // for } // Check for all participants wheter he is in the explosion area void HandlerParticipantArea::calculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data) { auto it = m_aesItemViews.find(data.aesItemId()); if (it == m_aesItemViews.end()) { WARNING_LOG(m_logger) << "Could not find item view for aes with id " << data.aesItemId(); return; } CentralEffectSimulation::AesItemViewUtm &aesItemView = *it; // older then 10sec? ignore if (data.sampleTime() < m_ddsHandler->currentPartitionTime() - 10 * 1000) { if (aesItemView.sampleTimeHandledImpacts < data.sampleTime()) { aesItemView.sampleTimeHandledImpacts = data.sampleTime(); aesItemView.handledImpacts = data.bitfield(); } return; } QVector bitsToCheck; aesItemView.handledImpacts.resize(data.bitfield().size(), 0xFF); for (int bitFieldIdx = 0; bitFieldIdx < data.impactCount(); ++bitFieldIdx) { // not yet triggered (bit is still 1) if (LtsUtils::BitArray::testBit(data.bitfield(), bitFieldIdx)) continue; // already checked (bit is already 0) if (!LtsUtils::BitArray::testBit(aesItemView.handledImpacts, bitFieldIdx)) continue; DEBUG_LOG(m_logger) << "New impact at idx " << bitFieldIdx; bitsToCheck += bitFieldIdx; } // remember handled impacts aesItemView.sampleTimeHandledImpacts = data.sampleTime(); aesItemView.handledImpacts = data.bitfield(); const auto participantPositions = m_handlerParticipant->positions(); for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos : participantPositions) { if (pPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill) continue; if (pPos.insideBuilding()) continue; if (!inBoundingBoxCheck(aesItemView, pPos)) continue; calculateImpacts(aesItemView, bitsToCheck, pPos); } } // calculateImpacts // calculate all impacts of the field for a participant void HandlerParticipantArea::calculateImpacts(const CentralEffectSimulation::AesItemViewUtm &itemView, const QVector &impactsToCheck, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition) { const bool bIsSmartChargeType = m_ddsHandler->isSmartChargeType(itemView.chargeTypeId); DEBUG_LOG(m_logger) << "calculateImpacts, isSMart: " << bIsSmartChargeType; // get radius of munition const double impactRadius = m_ddsHandler->chargeTypeRadius(itemView.chargeTypeId); const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone); for (int bitFieldIdx : impactsToCheck) { if (itemView.form.size() <= bitFieldIdx) continue; if (bIsSmartChargeType) { continue; } // get position of impact to corresponding bit const QPointF &impactPos = itemView.form.value(bitFieldIdx); if (inCircle(impactPos, impactRadius, participantPosUtmPoint)) sendEffectTypeEvent(itemView, participantPosition, EffectType::ExplosionArea, bitFieldIdx, QDateTime::currentMSecsSinceEpoch()); } // foreach Impact } // calculateImpacts // Send via TAB-Proxy to battlefield void HandlerParticipantArea::sendCesHitRequest(const CentralEffectSimulation::AesItemViewUtm &itemView, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition, qint64 effectTime, int remaining, int mineFieldBitIndex) { int32_t legacyChargeTypeId = 0; legacyChargeTypeId = m_ddsHandler->getLegacyIdFromChargeTypeId(itemView.chargeTypeId); if (remaining < 0) return; DEBUG_LOG(m_logger) << "sendCesHitRequest Settings::calculateCentralAes=" << std::boolalpha << Settings::calculateCentralAes() << " insideWlsArea=" << std::boolalpha << participantPosition.insideWlsArea() << " isSmartChargeType=" << std::boolalpha << m_ddsHandler->isSmartChargeType(itemView.chargeTypeId) << " participantId=" << participantPosition.participantId(); if (!m_ddsHandler->isSmartChargeType(itemView.chargeTypeId)) { DEBUG_LOG(m_logger) << "don't sendCesHitRequest Smart Type return"; return; } const int equipNo = m_ddsHandler->participantId2EquipNo(participantPosition.participantId()); DEBUG_LOG(m_logger) << " ftc sendCESHit to Participant participantId = " << participantPosition.participantId() << " equipNo = " << equipNo << " aesItemId =" << itemView.aesItemId; // participantPosition.participantPosition == 0 -> send to all if (participantPosition.participantId() != 0 && equipNo <= 0) { DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 && equipNo <= 0" ; return; } if (participantPosition.participantId() != 0 && participantPosition.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill) { DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 CatastrophicKill)"; return; } int32_t originatorParticipantId = m_aesItemId2partIdMap.value(itemView.aesItemId, 0); const TrainingAreaBase::TabPosition pos(participantPosition.pos(), Core::PositionReportingSource_def::Gps, // irrelevant participantPosition.buildingId(), participantPosition.roomId()); const int originatorEquipNo = m_ddsHandler->participantId2EquipNo(originatorParticipantId); TrainingAreaBase::SendCesHitRequest requestTab( equipNo, // equipmentNumber participantPosition.participantId(), // participantId itemView.chargeTypeId, // chargeTypeId itemView.aesItemId, // aesId originatorEquipNo, // originatorEquipmentNumber remaining, // remainEffectPercent ( no protection outside Building ) pos, effectTime, // effectTime itemView.partyId, // originPartyId mineFieldBitIndex ); DEBUG_LOG(m_logger) << " participantId=" << participantPosition.participantId() << " chargeTypeId=" << itemView.chargeTypeId << " legacyChargeTypeId=" << legacyChargeTypeId; m_sendCesHitRequestQueue.enqueue(requestTab); if (m_sendCesHitRequestQueue.size() == 1) { DEBUG_LOG(m_logger) << "SendCesHitRequest: " << requestTab; m_ddsHandler->ftcTab()->sendCesHitRequest(requestTab, this); } } void HandlerParticipantArea::sendCesHitRequest(const AreaEffectSimulation::AesItemView &itemView, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition, qint64 effectTime, int remaining, int mineFieldBitIndex) { if (remaining < 0) return; int32_t legacyChargeTypeId = 0; legacyChargeTypeId = m_ddsHandler->getLegacyIdFromChargeTypeId(itemView.chargeTypeId()); DEBUG_LOG(m_logger) << "sendCesHitRequest Settings::calculateCentralAes=" << std::boolalpha << Settings::calculateCentralAes() << " insideWlsArea=" << std::boolalpha << participantPosition.insideWlsArea() << " isSmartChargeType=" << std::boolalpha << m_ddsHandler->isSmartChargeType(itemView.chargeTypeId()) << " participantId=" << participantPosition.participantId(); const int equipmentNumber = m_ddsHandler->participantId2EquipNo(participantPosition.participantId()); DEBUG_LOG(m_logger) << " ftc sendCESHit to Participant participantId = " << participantPosition.participantId() << " equipNo = " << equipmentNumber << " aesItemId =" << itemView.aesItemId(); // participantPosition.participantPosition == 0 -> send to all if (participantPosition.participantId() != 0 && equipmentNumber <= 0) { DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 && equipNo <= 0" ; return; } // if if (participantPosition.participantId() != 0 && participantPosition.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill) { DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 CatastrophicKill)"; return; } // if int32_t originatorParticipantId = m_aesItemId2partIdMap.value(itemView.aesItemId(), 0); const int originatorEquipmentNumber = m_ddsHandler->participantId2EquipNo(originatorParticipantId); const TrainingAreaBase::TabPosition pos( participantPosition.pos(), Core::PositionReportingSource_def::Gps, participantPosition.buildingId(), participantPosition.roomId() ); TrainingAreaBase::SendCesHitRequest requestTab( equipmentNumber, participantPosition.participantId(), legacyChargeTypeId, itemView.aesItemId(), originatorEquipmentNumber, remaining, pos, effectTime, itemView.partyId().get(), mineFieldBitIndex ); DEBUG_LOG(m_logger) << " participantId=" << participantPosition.participantId() << " chargeTypeId=" << itemView.chargeTypeId() << " legacyChargeTypeId=" << legacyChargeTypeId; m_sendCesHitRequestQueue.enqueue(requestTab); if (m_sendCesHitRequestQueue.size() == 1) { DEBUG_LOG(m_logger) << "SendCesHitRequest: " << requestTab; m_ddsHandler->ftcTab()->sendCesHitRequest(requestTab, this); } // if } void HandlerParticipantArea::sendParticipantEvent(const CentralEffectSimulation::AesItemViewUtm &itemView, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition, const ProjectDataManagement::EventType eventType) { bool calculateAes = false; ParticipantManagement::TypeClass typeClass = m_handlerParticipant->typeClassFromTypeId(participantPosition.participantTypeId()); // can happen since the positions are collected for all participants (no check if it is a SupportEntity or similar) if (typeClass == ParticipantManagement::TypeClass_def::Unit || typeClass == ParticipantManagement::TypeClass_def::SupportUnit || typeClass == ParticipantManagement::TypeClass_def::SupportEntity || typeClass == ParticipantManagement::TypeClass_def::Aircraft || typeClass == ParticipantManagement::TypeClass_def::Vessel || typeClass == ParticipantManagement::TypeClass_def::Unknown) { DEBUG_LOG(m_logger) << "Participant with id " << participantPosition.participantId() << " has invalid typeClass: " << typeClass << " eventType: " << eventType; return; } if (!eventMustBeSend(itemView.aesItemId, participantPosition.participantId(), eventType)) { DEBUG_LOG(m_logger) << "Not sending Event " << eventType << " for participant " << participantPosition.participantId() << " and aes " << itemView.aesItemId << " - already sent"; return; } switch (Settings::calculateCentralAes()) { case Settings::NO_AES_CALCUALTION: calculateAes = false; break; case Settings::AES_CALCUALTION: calculateAes = true; break; case Settings::AES_CALCUALTION_ONLY_IN_WLS_AREA: if (participantPosition.insideWlsArea()) calculateAes = true; else { DEBUG_LOG(m_logger) << "!participantPosition.insideWlsArea()"; calculateAes = false; } } // send hit events only with central AES Simulation if (calculateAes && (eventType == ProjectDataManagement::EventType_def::HitB || eventType == ProjectDataManagement::EventType_def::HitC || eventType == ProjectDataManagement::EventType_def::Hit || eventType == ProjectDataManagement::EventType_def::HitEffectSimulation || eventType == ProjectDataManagement::EventType_def::HitMine || eventType == ProjectDataManagement::EventType_def::HitN)) { DEBUG_LOG(m_logger) << "send hit events only with central AES Simulation participant=" << participantPosition.participantId(); return; } int equipmentNumber = m_ddsHandler->participantId2EquipNo(participantPosition.participantId()); if (equipmentNumber <= 0) { WARNING_LOG(m_logger) << "Not sending Event " << eventType << " for participant " << participantPosition.participantId() << " - participant has no equipment number"; return; } DEBUG_LOG(m_logger) << "sendParticipantEvent" << eventType << " participantId=" << participantPosition.participantId() << " AesItemId=" << itemView.aesItemId; CentralEffectSimulation::ParticipantEvent pe; pe.id(m_ddsHandler->getParticipantEventId()); pe.eventTypeId(eventType.underlying()); pe.participantId(participantPosition.participantId()); pe.eventTime(participantPosition.sampleTime()); pe.aesItemId(itemView.aesItemId); pe.partyId(participantPosition.partyId()); //pe.aesName(m_handlerAesData->aesName(itemView.aesItemId)); pe.equipmentNumber(equipmentNumber); pe.vulnerability(participantPosition.vulnerabilityState.underlying()); pe.position(participantPosition.pos()); // Criterion for friendly Fire: if (itemView.partyId != -1 && ((eventType == ProjectDataManagement::EventType_def::EnterMineArea || eventType == ProjectDataManagement::EventType_def::EnterNuclear || eventType == ProjectDataManagement::EventType_def::HitB || eventType == ProjectDataManagement::EventType_def::HitC || eventType == ProjectDataManagement::EventType_def::Hit || eventType == ProjectDataManagement::EventType_def::HitEffectSimulation || eventType == ProjectDataManagement::EventType_def::HitMine || eventType == ProjectDataManagement::EventType_def::HitN) && itemView.partyId == participantPosition.partyId())) pe.friendlyFire(true); else pe.friendlyFire(false); INFO_LOG(m_logger) << "sendParticipantEvent: " << pe << ", currentMSecsSinceEpoch = " << QDateTime::currentMSecsSinceEpoch() << ", time difference = " << QDateTime::currentMSecsSinceEpoch() - pe.eventTime(); m_ddsHandler->dtsCes()->publish(pe); const auto id = pe.id(); QTimer::singleShot(1 * 60 * 1000, this, [id, this]() { m_ddsHandler->dtsCes()->removeParticipantEventByKey(id); }); } void HandlerParticipantArea::sendEffectTypeEvent(const CentralEffectSimulation::AesItemViewUtm &itemView, const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition, EffectType effectType, int mineFieldBitIndex, qint64 effectTime) { Q_UNUSED(effectTime); // Trefferereignis Sekundaerwirkung switch (Settings::calculateCentralAes()) { case Settings::NO_AES_CALCUALTION: return; case Settings::AES_CALCUALTION: break; case Settings::AES_CALCUALTION_ONLY_IN_WLS_AREA: if (!participantPosition.insideWlsArea()) { DEBUG_LOG(m_logger) << "!participantPosition.insideWlsArea()"; return; } } /*if (!participantPosition.insideWlsArea()) { // forget all enter/leave - the TNE calculates on its own m_effectTypeStates[itemView.aesItemId][participantPosition.participantId()].clear(); m_effectTypeBitFieldIndexes[itemView.aesItemId][participantPosition.participantId()].clear(); return; }*/ QByteArray mfb; if (mineFieldBitIndex >= 0) mfb = ", mineFieldBitIndex: " + QByteArray::number(mineFieldBitIndex); if (!effectTypeMustBeSend(itemView.aesItemId, participantPosition.participantId(), effectType, mineFieldBitIndex, participantPosition.insideBuilding())) { DEBUG_LOG(m_logger) << "Not sending EffectType event " << toString(effectType) << ", participant: " << participantPosition.participantId() << ", aesItemId: " << itemView.aesItemId << mfb << " - already sent"; return; } const int ctasEffectType = static_cast(effectType) & 0xffff; DEBUG_LOG(m_logger) << "Sending EffectType event " << toString(effectType) << ", participant: " << participantPosition.participantId() << ", aesItemId: " << itemView.aesItemId << mfb << ", ctasEffectType: " << ctasEffectType; sendCesHitRequest(itemView, participantPosition, //effectTime >= 0 ? effectTime : participantPosition.sampleTime(), QDateTime::currentMSecsSinceEpoch(), ctasEffectType, mineFieldBitIndex); } bool HandlerParticipantArea::eventMustBeSend(int aesItemId, int participantId, const ProjectDataManagement::EventType eventType) { // end enter-leave only once QSet &activeEvents = m_eventStates[aesItemId][participantId]; // reference! // - only send event if it was not already sent if (activeEvents.contains(eventType)) { DEBUG_LOG(m_logger) << "activeEvents=" << eventType; return false; } activeEvents.insert(eventType); // - remove opposite events (e.g remove Enter event when we leave the area) switch (eventType.underlying()) { case ProjectDataManagement::EventType_def::EnterDangerArea: activeEvents.remove(ProjectDataManagement::EventType_def::LeaveDangerArea); break; case ProjectDataManagement::EventType_def::LeaveDangerArea: activeEvents.remove(ProjectDataManagement::EventType_def::EnterDangerArea); break; case ProjectDataManagement::EventType_def::EnterMineArea: activeEvents.remove(ProjectDataManagement::EventType_def::LeaveMineArea); break; case ProjectDataManagement::EventType_def::LeaveMineArea: activeEvents.remove(ProjectDataManagement::EventType_def::EnterMineArea); break; case ProjectDataManagement::EventType_def::EnterFunctionalArea: activeEvents.remove(ProjectDataManagement::EventType_def::LeaveFunctionalArea); break; case ProjectDataManagement::EventType_def::LeaveFunctionalArea: activeEvents.remove(ProjectDataManagement::EventType_def::EnterFunctionalArea); break; case ProjectDataManagement::EventType_def::EnterNuclear: activeEvents.remove(ProjectDataManagement::EventType_def::LeaveNuclear); break; case ProjectDataManagement::EventType_def::LeaveNuclear: activeEvents.remove(ProjectDataManagement::EventType_def::EnterNuclear); break; default: break; } return true; } bool HandlerParticipantArea::effectTypeMustBeSend(int aesItemId, int participantId, EffectType effectType, int mineFieldBitIndex, bool insideBuilding) { // end enter-leave only once QSet &activeEffects = m_effectTypeStates[aesItemId][participantId]; // reference! // - only send event if it was not already sent if (activeEffects.contains(effectType)) { DEBUG_LOG(m_logger) << "effectTypeStates already contains aesItemId " << aesItemId; return false; } activeEffects += effectType; bool bMustBeSend = true; switch (effectType) { case EffectType::EffectiveMineRangeEnter: { activeEffects.remove(EffectType::EffectiveMineRangeLeave); activeEffects.remove(EffectType::EffectiveMineRangeEnter); // must check per bit, not per area QSet &mineFieldBits = m_effectTypeBitFieldIndexes[aesItemId][participantId]; if (mineFieldBits.contains(mineFieldBitIndex) || insideBuilding) bMustBeSend = false; else { mineFieldBits.insert(mineFieldBitIndex); bMustBeSend = true; } break; } case EffectType::EffectiveMineRangeLeave: { activeEffects.remove(EffectType::EffectiveMineRangeEnter); activeEffects.remove(EffectType::EffectiveMineRangeLeave); // must check per bit, not per area QSet &mineFieldBits = m_effectTypeBitFieldIndexes[aesItemId][participantId]; mineFieldBits.remove(mineFieldBitIndex); bMustBeSend = false; // no need to send leave to CTAS break; } case EffectType::NContaminationLeave: bMustBeSend = false; bMustBeSend |= activeEffects.remove(EffectType::NContaminationEnterArea1); bMustBeSend |= activeEffects.remove(EffectType::NContaminationEnterArea2); bMustBeSend |= activeEffects.remove(EffectType::NContaminationDetonation); break; case EffectType::NContaminationEnterArea1: bMustBeSend = true; activeEffects.remove(EffectType::NContaminationLeave); activeEffects.remove(EffectType::NContaminationEnterArea2); activeEffects.remove(EffectType::NContaminationDetonation); break; case EffectType::NContaminationEnterArea2: bMustBeSend = true; activeEffects.remove(EffectType::NContaminationLeave); activeEffects.remove(EffectType::NContaminationEnterArea1); activeEffects.remove(EffectType::NContaminationDetonation); break; case EffectType::NContaminationDetonation: bMustBeSend = true; activeEffects.remove(EffectType::NContaminationLeave); activeEffects.remove(EffectType::NContaminationEnterArea1); activeEffects.remove(EffectType::NContaminationEnterArea2); break; case EffectType::FunctionalAreaLeave: bMustBeSend = activeEffects.remove(EffectType::FunctionalAreaEnter); break; case EffectType::FunctionalAreaEnter: activeEffects.remove(EffectType::FunctionalAreaLeave); break; case EffectType::ExplosionArea: activeEffects.remove(EffectType::ExplosionArea); bMustBeSend = true; break; case EffectType::ExplosionSingle: bMustBeSend = true; break; case EffectType::BCContaminationLeave: bMustBeSend = false; bMustBeSend |= activeEffects.remove(EffectType::BCContaminationEnterFloating); bMustBeSend |= activeEffects.remove(EffectType::BCContaminationEnterResident); bMustBeSend |= activeEffects.remove(EffectType::BCContaminationEnterFloatingArea); break; case EffectType::BCContaminationEnterFloating: bMustBeSend = true; activeEffects.remove(EffectType::BCContaminationLeave); activeEffects.remove(EffectType::BCContaminationEnterResident); activeEffects.remove(EffectType::BCContaminationEnterFloatingArea); break; case EffectType::BCContaminationEnterResident: bMustBeSend = true; activeEffects.remove(EffectType::BCContaminationLeave); activeEffects.remove(EffectType::BCContaminationEnterFloating); activeEffects.remove(EffectType::BCContaminationEnterFloatingArea); break; case EffectType::BCContaminationEnterFloatingArea: bMustBeSend = true; activeEffects.remove(EffectType::BCContaminationLeave); activeEffects.remove(EffectType::BCContaminationEnterFloating); activeEffects.remove(EffectType::BCContaminationEnterResident); break; case EffectType::BeStillWarning: case EffectType::Unknown: break; } return bMustBeSend; } // Anmerkung: Im SFM werden Quadrate und keine Kreise genommen. ( Wird hier nach Absprache mit Manfred nicht gemacht) bool HandlerParticipantArea::inCircle(const QPointF ¢erUtm, const double radius, const QPointF &pointUtm) { double squareDist = CentralEffectSimulation::getSquareDistance(centerUtm, pointUtm); double squareRadius = radius * radius; DEBUG_LOG(m_logger) << "squareDist: " << squareDist << ", squareRadius: " << squareRadius; return (squareDist < squareRadius); } // inCircle ProjectDataManagement::EventType HandlerParticipantArea::leaveEventTypeForAesType(AreaEffectSimulation::AesType aesTypeId) { switch (aesTypeId.underlying()) { case AreaEffectSimulation::AesType_def::AtDangerArea: return ProjectDataManagement::EventType::LeaveDangerArea; case AreaEffectSimulation::AesType_def::AtMineArea: return ProjectDataManagement::EventType::LeaveMineArea; case AreaEffectSimulation::AesType_def::AtNContamination: return ProjectDataManagement::EventType::LeaveNuclear; case AreaEffectSimulation::AesType_def::AtFunctionalArea: return ProjectDataManagement::EventType::LeaveFunctionalArea; default: break; } return ProjectDataManagement::EventType::Unknown; } EffectType HandlerParticipantArea::leaveEffectTypeForAesType(AreaEffectSimulation::AesType aesTypeId) { switch (aesTypeId.underlying()) { case AreaEffectSimulation::AesType_def::AtBCContamination: return EffectType::BCContaminationLeave; case AreaEffectSimulation::AesType_def::AtNContamination: return EffectType::NContaminationLeave; case AreaEffectSimulation::AesType_def::AtFunctionalArea: return EffectType::FunctionalAreaLeave; default: break; } return EffectType::Unknown; } EffectType HandlerParticipantArea::effectTypeForNContaminationArea(const CentralEffectSimulation::AesItemViewUtm &itemView, const QPointF &participantPosUtmPoint, qint64 partPosSampleTime) { if (itemView.radials.size() != 3) return EffectType::NContaminationEnterArea2; // not yet known, we only know we're inside the area const double squareDist = CentralEffectSimulation::getSquareDistance(itemView.center, participantPosUtmPoint); if (squareDist <= pow(itemView.radials.at(0), 2)) { // 5s delay DEBUG_LOG(m_logger) << "partPosSampleTime: " << partPosSampleTime << ", itemView.effectStart: " << itemView.effectStart; if (partPosSampleTime < itemView.effectStart + 5000) return EffectType::NContaminationDetonation; return EffectType::NContaminationEnterArea1; } if (squareDist <= pow(itemView.radials.at(1), 2)) return EffectType::NContaminationEnterArea1; if (squareDist <= pow(itemView.radials.at(2), 2)) return EffectType::NContaminationEnterArea2; return EffectType::NContaminationLeave; } EffectType HandlerParticipantArea::effectTypeForBCContaminationArea(const CentralEffectSimulation::AesItemViewUtm &itemView, const QPointF &participantPosUtmPoint) { if (itemView.radials.size() != 1) return EffectType::BCContaminationEnterFloating; const double squareDist = CentralEffectSimulation::getSquareDistance(itemView.center, participantPosUtmPoint); if (squareDist <= pow(itemView.radials.at(0), 2)) return EffectType::BCContaminationEnterResident; // first circle of resident contamination return EffectType::BCContaminationEnterFloatingArea; // expanding/moving part of the contamination } void HandlerParticipantArea::aesItemViewRemoved(int aesItemId) { const CentralEffectSimulation::AesItemViewUtm aiv = m_aesItemViews.value(aesItemId); if (aiv.aesItemId == aesItemId) { const auto effectTypLeave = leaveEffectTypeForAesType(aiv.aesTypeId); if (effectTypLeave != EffectType::Unknown) { DEBUG_LOG(m_logger) << "aesItemViewRemoved, leaveEffectType: " << toString(effectTypLeave); auto it = m_effectTypeStates.find(aesItemId); if (it != m_effectTypeStates.end()) { const QHash> &participants = it.value(); DEBUG_LOG(m_logger) << "aesItemViewRemoved, sending leave events to " << participants.size() << " participants"; for (auto itP = participants.begin(); itP != participants.end(); ++ itP) sendEffectTypeEvent(aiv, m_handlerParticipant->position(itP.key()), effectTypLeave, -1, -1); } } const auto eventTypeLeave = leaveEventTypeForAesType(aiv.aesTypeId); if (eventTypeLeave != ProjectDataManagement::EventType::Unknown) { DEBUG_LOG(m_logger) << "aesItemViewRemoved, leaveEventType: " << eventTypeLeave; auto it = m_eventStates.find(aesItemId); if (it != m_eventStates.end()) { const QHash> &participants = it.value(); DEBUG_LOG(m_logger) << "aesItemViewRemoved, sending leave events to " << participants.size() << " participants"; for (auto itP = participants.begin(); itP != participants.end(); ++ itP) sendParticipantEvent(aiv, m_handlerParticipant->position(itP.key()), eventTypeLeave); } } } m_effectTypeStates.remove(aesItemId); m_effectTypeBitFieldIndexes.remove(aesItemId); m_aesItemViews.remove(aesItemId); m_explosionSingleStates.remove(aesItemId); m_eventStates.remove(aesItemId); } } // namespace Ces2D