/** * Handler for all exercise objects * $Id$ * $HeadURL$ * $Author$ * Created by Hanna E. Rathje, SLSE, 2015-09-03 */ #include "handler_mine_area.h" #include "Utils/polygons.h" #include "handler_effect_cluster.h" #include "handler_mine_corridor.h" #include "object_mine_area_null.h" #include "settings.h" #include #include #include #include #include namespace AreaEffectSimulation { HandlerMineArea::HandlerMineArea(MainHandler *mainHandler) : HandlerBaseTmpl(mainHandler, QStringLiteral("MineArea"), s_areaCount[AesType::AtMineArea], s_areaCountCentral[AesType::AtMineArea]) , Interface::Fts::MineAreaModifyRequestHandler(mainHandler->dds()->fts()) , Interface::Fts::MineAreaRemoveRequestHandler(mainHandler->dds()->fts()) , Interface::Fts::MineResetRequestHandler(mainHandler->dds()->fts()) , Interface::Fts::MineTriggeredRequestHandler(mainHandler->dds()->fts()) , Interface::Fts::MineAreaModifyFromCtaDataRequestHandler(mainHandler->dds()->fts()) , Interface::Fts::MineAreaReferenceModifyRequestHandler(mainHandler->dds()->fts()) , m_nullArea(new ObjectMineAreaNull(mainHandler, this)) { } bool HandlerMineArea::isDusMineArea(int chargeTypeId, int igniterTypeId, int igniterFunctionId) const { return static_cast(m_mainHandler->dds()->pdmDtc()->mineClass(chargeTypeId, igniterTypeId, igniterFunctionId)) == MineClass::DUS; } bool HandlerMineArea::isConcreteMine(int chargeTypeId, int igniterTypeId, int igniterFunctionId) const { return static_cast(m_mainHandler->dds()->pdmDtc()->mineClass(chargeTypeId, igniterTypeId, igniterFunctionId)) == MineClass::Concrete; } bool HandlerMineArea::isFromMineLayer(int chargeTypeId, int igniterTypeId, int igniterFunctionId) const { return m_mainHandler->dds()->pdmDtc()->isFromMineLayer(chargeTypeId, igniterTypeId, igniterFunctionId); } bool HandlerMineArea::isSingleVirtualMine(int chargeTypeId, int igniterTypeId, int igniterFunctionId) const { return static_cast(m_mainHandler->dds()->pdmDtc()->mineClass(chargeTypeId, igniterTypeId, igniterFunctionId)) == MineClass::VirtualSingle; } bool HandlerMineArea::isVirtualMine(int chargeTypeId, int igniterTypeId, int igniterFunctionId) const { return static_cast(m_mainHandler->dds()->pdmDtc()->mineClass(chargeTypeId, igniterTypeId, igniterFunctionId)) == MineClass::Virtual; } bool HandlerMineArea::init() { QVector areaVec; // read all MineAreas from database if (!m_mainHandler->db()->select(areaVec)) { ERROR_LOG(m_logger) << "Error reading data from database!"; return false; } // create area mine objects for (const Database::AesMineArea &area : qAsConst(areaVec)) { if (area.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery && !area.isCentral && (area.effectStateId == AesEffectState::EsPrepared || area.effectStateId == AesEffectState::EsActivated)) ++m_totalPzAbwWMis; // Create and init Object ObjectMineArea *ifs(new ObjectMineArea(m_mainHandler, this)); connect(ifs, &ObjectMineArea::effectStateChanged, this, &HandlerMineArea::onEffectStateChanged); Core::FunctionReplyObject ret = ifs->createObject(area); if (ret.error()) { delete ifs; ERROR_LOG(m_logger) << ret(); return false; } // Check concrete Mines on lastest value if (isConcreteMine(area.chargeTypeId, area.igniterTypeId, area.igniterFunctionId) || isDusMineArea(area.chargeTypeId, area.igniterTypeId, area.igniterFunctionId)) { if (ifs->lastMineNo() > s_startPzAbwWMiCCount) s_startPzAbwWMiCCount = ifs->lastMineNo(); } // If active or prepared, check if it still can be handled and increment area count if (!ifs->data().isCentral) { if (totalAreas() >= s_maxActiveAreas[AesType::AtMineArea]) { ERROR_LOG(m_logger) << "More active mine areas in database than allowed - ignoring the rest!"; ifs->removeObject(); delete ifs; continue; } } else { if (totalAreasCentral() >= s_maxActiveAreasCentral[AesType::AtMineArea]) { ERROR_LOG(m_logger) << "More active mine areas in database than allowed - ignoring the rest!"; ifs->removeObject(); delete ifs; continue; } } if (ifs->isObjectInTrainingArea()) { if (ifs->data().isCentral) ++m_areaCountCentral; else ++m_areaCount; } // Push into Map m_objectMap.insert(ifs->id(), ifs); connect(ifs, &ObjectMineArea::newAiv, this, &HandlerMineArea::newAiv); connect(ifs, &ObjectMineArea::removeAiv, this, &HandlerMineArea::removeAiv); connect(ifs, &ObjectMineArea::releaseMines, this, &HandlerMineArea::redistributeMines); connect(ifs, &ObjectMineArea::shapeChanged, this, &HandlerMineArea::assignMinesFromNullArea); // self trigger EffectStateChangedSlot or DUS-Mines from recently Expired MineAreas arent't released ifs->onEffectStateChanged(); } if (m_nullArea->lastConcreteMineNo() > s_startPzAbwWMiCCount) s_startPzAbwWMiCCount = m_nullArea->lastConcreteMineNo(); DEBUG_LOG(m_logger) << "s_startPzAbwWMiCCount: " << s_startPzAbwWMiCCount; connect(m_mainHandler->dds()->tabDtc().get(), &TrainingAreaBase::Interface::Dtc::mineTriggeredChanged, this, &HandlerMineArea::onMineTriggered); m_mainHandler->dds()->tabDtc()->enableMineTriggered(); connect(m_mainHandler->dds()->tabDtc().get(), &TrainingAreaBase::Interface::Dtc::minelayerTrailerPositionChanged, this, &HandlerMineArea::onMinelayerPositionChanged); m_mainHandler->dds()->tabDtc()->enableMinelayerTrailerPosition(); connect(m_mainHandler->mineCorridorHandler().get(), &HandlerBase::newAiv, this, &HandlerMineArea::onNewMineCorridor); DEBUG_LOG(m_logger) << "m_exeObjMap.size() = " << m_objectMap.size(); return true; } void HandlerMineArea::redistributeMines(int aesId, QList mines) { if (!m_objectMap.contains(aesId)) return; DEBUG_LOG(m_logger) << "Putting mines from " << aesId << " into other MineAreas if applicable or in NullArea."; // check if any other MineArea will handle some of the mines for (const auto &mineArea : qAsConst(m_objectMap)) { if (mineArea->id() != aesId) { QMutableListIterator it(mines); while (it.hasNext()) { auto mine = it.next(); if (mineArea->canHandleMine(mine.mineId, mine.chargeTypeId, mine.position)) { DEBUG_LOG(m_logger) << "Assigning mine " << mine.mineId << " to mine Area " << mineArea->id(); mineArea->updateMine(mine); it.remove(); } } } } DEBUG_LOG(m_logger) << mines.size() << " unhandled mines will be assigned to NullArea"; // anything not handled is added to NullArea if (!mines.isEmpty()) m_nullArea->triggerMines(mines); } bool HandlerMineArea::removeDuplicateMine(int mineId) { for (const auto &mineArea : qAsConst(m_objectMap)) { if (mineArea->isDusMineArea() && mineArea->hasMine(mineId)) { mineArea->takeMine(mineId); return true; } } return false; } int HandlerMineArea::totalAreas() const { return s_areaCount[AesType::AtMineArea]; } int HandlerMineArea::totalAreasCentral() const { return s_areaCountCentral[AesType::AtMineArea]; } template T getValueHelper(T in) { return in; } template T getValueHelper(const dds::core::optional &in) { return in.is_set() ? in.get() : T(); } template static void fillData(const T &src, Database::AesMineArea &dst) { if (src.id() < 0) // is the id given as negative? set id -> used by OC-Laptop dst.id = src.id(); dst.name = QString::fromStdString(src.name()); dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime(); dst.effectDuration = src.effectDuration(); dst.effectSourceId = src.effectSourceId(); dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0; dst.chargeTypeId = src.chargeStruct().chargeTypeId(); dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0; dst.density = src.density(); dst.width = src.width(); dst.layingProcedureTypeId = src.layingProcedureType(); dst.isLaidHidden = src.isLaidHidden(); dst.effectMarkingInTa = src.effectMarkingInTa(); dst.isCenterLine = src.isCenterLine(); dst.externalId = getValueHelper(src.externalId()); } static inline bool intersects(const QPolygonF &p1, const std::vector &ddsp2) { QPolygonF p2(ddsp2.size()); int i = 0; for (const auto &point : ddsp2) p2[i++] = QPointF(point.lon(), point.lat()); QPainterPath pp1; pp1.addPolygon(p1); QPainterPath pp2; pp2.addPolygon(p2); return pp1.intersects(pp2); } void HandlerMineArea::onMineAreaModifyRequest(const rti::core::SampleIdentity &cookie, const MineAreaModifyRequest &request) { DEBUG_LOG(m_logger) << "onMineAreaModifyRequest: " << request << "totalAreas=" << totalAreas() << " s_maxActiveAreas[AesType::AtMineArea]=" << s_maxActiveAreas[AesType::AtMineArea]; const Core::FunctionReplyObject ret = mineAreaModifyRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->dds()->fts()->mineAreaModifyReply(cookie, ret); } template Core::FunctionReplyObject HandlerMineArea::mineAreaCreateRequest(const T &request) { // if ( !isDusMineArea(request.chargeTypeId()) // && (totalAreas() >= s_maxActiveAreas[AesType::AtMineArea] && request.effectStart().is_set())) // return Core::FunctionReplyObject(1, tr_lts("dds", "Too many peripheral mine areas.")); Database::AesMineArea newData; fillData(request, newData); // Check in advance if mines in null area are in the new mine area if (isDusMineArea(newData.chargeTypeId, newData.igniterTypeId, newData.igniterFunctionId) || isConcreteMine(newData.chargeTypeId, newData.igniterTypeId, newData.igniterFunctionId)) { if (!checkIfNullMineIsInArea(request.segments(), newData, request.distance())) { QString chargeTypeName = QString::fromStdString(m_mainHandler->dds()->pdmDtc()->chargeType(newData.chargeTypeId).name()); return Core::FunctionReplyObject(1, tr_lts("dds", "No mines of type '%1' found in chosen area")).arg(chargeTypeName); } } // check: effectStart > time(NULL) ? ObjectMineArea *ifs(new ObjectMineArea(m_mainHandler, this)); connect(ifs, &ObjectMineArea::effectStateChanged, this, &HandlerMineArea::onEffectStateChanged); Core::FunctionReplyObject ret = ifs->createObject(newData, request.segments()); DEBUG_LOG(m_logger) << ">>> mineAreaCreateRequest: re.segSize: " << request.segments().size() << " // ifs.segSize: " << ifs->segments().size() << " // mineCount: " << ifs->mineCount(); if (ret.error()) { delete ifs; return ret; } auto poly = Polygons::mineArea(ifs->segments(), newData.width, newData.density, newData.isCenterLine); // FIXME:Is segements filled here // Check if mine field is in building only bool isGroundLevel = true; if (m_mainHandler->dds()->ismDtc()->getSurroundingBuildingByPolygon(m_mainHandler->dds()->exerciseId(), poly, isGroundLevel) > 0) { ifs->removeObject(); // Not finally deleted from db after this,Fallback if DB deletion is not working correct ifs->removeObjectFromDb(); delete ifs; return Core::FunctionReplyObject(1, tr_lts("dds", "The mine field is within a building")); } std::vector _form; ObjectBase::ddsVecFromPoly(poly, _form); bool isCentral = ifs->checkIsCentral(_form); if (isCentral) { if (totalAreasCentral() >= s_maxActiveAreasCentral[AesType::AtMineArea] && request.effectStart().is_set()) { ifs->removeObject(); // Not finally deleted from db after this,Fallback if DB deletion is not working correct ifs->removeObjectFromDb(); delete ifs; return Core::FunctionReplyObject(1, tr_lts("dds", "Too many central mine areas.")); } else if (ifs->mineCount() > s_maxCentralSingleMinesCount) { ifs->removeObject(); // Not finally deleted from db after this,Fallback if DB deletion is not working correct ifs->removeObjectFromDb(); delete ifs; return Core::FunctionReplyObject(1, tr_lts("dds", "Too many central single mines.")); } } else { if (ifs->mineCount() > s_maxSingleMinesCount) // > 1200 { ifs->removeObject(); // Not finally deleted from db after this,Fallback if DB deletion is not working correct ifs->removeObjectFromDb(); delete ifs; return Core::FunctionReplyObject(1, tr_lts("dds", "Too many single mines.")); } int pzAbwWMiSafe = s_safePzAbwWMiCount - m_totalPzAbwWMis; if (newData.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery /*PzAbwWmiV */) { if (request.effectStart().is_set()) --pzAbwWMiSafe; } if (pzAbwWMiSafe < 0) pzAbwWMiSafe = 0; if (totalAreas() >= s_maxActiveAreas[AesType::AtMineArea] - pzAbwWMiSafe && request.effectStart().is_set()) { ifs->removeObject(); // Not finally deleted from db after this,Fallback if DB deletion is not working correct ifs->removeObjectFromDb(); delete ifs; return Core::FunctionReplyObject(1, tr_lts("dds", "Too many peripheral mine areas.")); } if (newData.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery /*PzAbwWmiV */) { if (request.effectStart().is_set()) ++m_totalPzAbwWMis; } } connect(ifs, &ObjectMineArea::newAiv, this, &HandlerMineArea::newAiv); connect(ifs, &ObjectMineArea::removeAiv, this, &HandlerMineArea::removeAiv); connect(ifs, &ObjectMineArea::releaseMines, this, &HandlerMineArea::redistributeMines); connect(ifs, &ObjectMineArea::shapeChanged, this, &HandlerMineArea::assignMinesFromNullArea); ifs->clearBuildings(); m_objectMap.insert(ifs->id(), ifs); if (isCentral) { ++m_areaCountCentral; DEBUG_LOG(m_logger) << "s_areaCountCentral[AesType::AtMineArea] = " << m_areaCountCentral; } else if (ifs->isObjectInTrainingArea()) { ++m_areaCount; DEBUG_LOG(m_logger) << "s_areaCount[AesType::AtMineArea] = " << m_areaCount; } if (ifs->isDusMineArea() || ifs->isConcreteMine()) { assignMinesFromNullArea(ifs->id()); DEBUG_LOG(m_logger) << "Start change to Line: " << request.setMineSorted(); ret = ifs->changeSegmentsToLine(request.setMineSorted()); } return ret; } template Core::FunctionReplyObject HandlerMineArea::mineAreaModifyRequest(const T &request) { Core::FunctionReplyObject ret; ObjectMineArea *ifs = nullptr; if (getValueHelper(request.externalId()) > 0) ifs = getObjectFromMapByExternalId(m_objectMap, getValueHelper(request.externalId()), ret); if (!ifs) { if (request.id() == 0 || ((request.id() < 0) && !getObjectFromMap(m_objectMap, std::abs(request.id()), ret))) return mineAreaCreateRequest(request); ifs = getObjectFromMap(m_objectMap, std::abs(request.id()), ret); if (!ifs) return ret; } if (request.effectStart().is_set() && (request.effectStart().get() + request.effectDuration() < m_mainHandler->dds()->currentPartitionTime().toMSecsSinceEpoch())) return removeObject(ifs); Database::AesMineArea newData(ifs->data()); fillData(request, newData); QVector vecSegments; double totalLength = 0.0; ifs->createSegments(request.segments(), request.id(), vecSegments, totalLength); auto poly = Polygons::mineArea(vecSegments, newData.width, newData.density, newData.isCenterLine); // FIXME:Is segements filled here // Check if mine field is in building only bool isGroundLevel = true; if (m_mainHandler->dds()->ismDtc()->getSurroundingBuildingByPolygon(m_mainHandler->dds()->exerciseId(), poly, isGroundLevel) > 0) return Core::FunctionReplyObject(1, tr_lts("dds", "The mine field is within a building")); std::vector _form; ObjectBase::ddsVecFromPoly(poly, _form); bool isCentral = ifs->checkIsCentral(_form); /*! * If the effect start added after creation, the max amount need to be checked */ if (!ifs->isActivationTimeSet() && request.effectStart().is_set()) { if (isCentral) { if (totalAreasCentral() >= s_maxActiveAreasCentral[AesType::AtMineArea] /*&& request.effectStart().is_set()*/) return Core::FunctionReplyObject(1, tr_lts("dds", "Too many central mine areas.")); ret = ifs->checkDensity(newData.density, totalLength, s_maxCentralSingleMinesCount); if (ret.error()) return ret; } else { if (ifs->mineCount() >= s_maxSingleMinesCount) // > 1200 return Core::FunctionReplyObject(1, tr_lts("dds", "Too many single mines.")); ret = ifs->checkDensity(newData.density, totalLength, s_maxSingleMinesCount); if (ret.error()) return ret; int pzAbwWMiSafe = s_safePzAbwWMiCount - m_totalPzAbwWMis; if (newData.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery /*PzAbwWmiV */) { --pzAbwWMiSafe; } if (pzAbwWMiSafe < 0) pzAbwWMiSafe = 0; if (totalAreas() >= s_maxActiveAreas[AesType::AtMineArea] - pzAbwWMiSafe /*&& request.effectStart().is_set()*/) return Core::FunctionReplyObject(1, tr_lts("dds", "Too many peripheral mine areas.")); if (newData.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery /*PzAbwWmiV */) { ++m_totalPzAbwWMis; } } } // For counting, only Elements being at least prepared are important: if (request.effectStart().is_set() && ifs->isObjectModifiable()) { // Calculate new polygon here. Not nice, though bool wasCentral = ifs->data().isCentral; // only change if element changed from central to training area or vice versa if (isCentral != wasCentral) { // Change from central to training area if (wasCentral) { int pzAbwWMiSafe = s_safePzAbwWMiCount - m_totalPzAbwWMis; if (newData.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery /*PzAbwWmiV */) { if (request.effectStart().is_set()) --pzAbwWMiSafe; } if (pzAbwWMiSafe < 0) pzAbwWMiSafe = 0; if (totalAreas() >= s_maxActiveAreas[AesType::AtMineArea] - pzAbwWMiSafe) // Minus PzAbwWmiV return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Too many peripheral mine areas.")); if (newData.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery /*&& newData.chargeTypeId == 216*/ /*PzAbwWmiV */) m_totalPzAbwWMis++; m_areaCount++; m_areaCountCentral--; DEBUG_LOG(m_logger) << "s_areaCount[AesType::AtMineArea] = " << m_areaCount; DEBUG_LOG(m_logger) << "s_areaCountCentral[AesType::AtMineArea] = " << m_areaCountCentral; } else // Change from training area to central { if (totalAreasCentral() >= s_maxActiveAreasCentral[AesType::AtMineArea]) return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Too many central mine areas.")); m_areaCount--; m_areaCountCentral++; if (newData.layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery) --m_totalPzAbwWMis; DEBUG_LOG(m_logger) << "s_areaCount[AesType::AtMineArea] = " << m_areaCount; DEBUG_LOG(m_logger) << "s_areaCountCentral[AesType::AtMineArea] = " << m_areaCountCentral; } } if (request.layingProcedureType() == AesMaLayingProcedureType::LptPioneer && ifs->get().layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery && !isCentral) { --m_totalPzAbwWMis; DEBUG_LOG(m_logger) << "Change from Artillery to Pioneer Total Art: " << m_totalPzAbwWMis << " Total All: " << totalAreas(); int pzAbwWMiSafe = s_safePzAbwWMiCount - m_totalPzAbwWMis; if (pzAbwWMiSafe < 0) pzAbwWMiSafe = 0; if (totalAreas() > s_maxActiveAreas[AesType::AtMineArea] - pzAbwWMiSafe) // Minus PzAbwWmiV { ++m_totalPzAbwWMis; return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Too many peripheral mine areas.")); } } else if (request.layingProcedureType() == AesMaLayingProcedureType::LptArtillery && ifs->get().layingProcedureTypeId == AesMaLayingProcedureType::LptPioneer && !isCentral) { ++m_totalPzAbwWMis; DEBUG_LOG(m_logger) << "Change from Pioneer to Artillery Total Art: " << m_totalPzAbwWMis << " Total All: " << totalAreas(); } } if (request.id() < 0 && checkStateForSfm(ifs)) { DEBUG_LOG(m_logger) << "Overwrite mine area Effect State for OC"; newData.effectStateId = AesEffectState::EsPrepared; } // check: effectStart > time(NULL) ? int32_t oldChargeTypeId = ifs->data().chargeTypeId; ret = ifs->updateObject(newData, request.segments()); // assign mines from NullArea if applicable if (oldChargeTypeId != newData.chargeTypeId) if (ifs->isDusMineArea() || ifs->isConcreteMine()) { assignMinesFromNullArea(ifs->id()); DEBUG_LOG(m_logger) << "Start change to Line(modify): " << request.setMineSorted(); ifs->changeSegmentsToLine(request.setMineSorted()); } ifs->clearBuildings(); return ret; } void HandlerMineArea::onMineAreaRemoveRequest(const rti::core::SampleIdentity &cookie, const Core::RemoveElementRequest &request) { Core::FunctionReplyObject ret; const qint32 id = request.id() < 0 ? (request.id() * -1) : request.id(); ObjectMineArea *ifs = getObjectFromMap(m_objectMap, id, ret); if (ifs == nullptr) { m_mainHandler->dds()->fts()->mineAreaRemoveReply(cookie, ret); return; } if (ifs->data().layingProcedureTypeId == AesMaLayingProcedureType::LptArtillery && ifs->isActivationTimeSet()) m_totalPzAbwWMis--; m_mainHandler->dds()->fts()->mineAreaRemoveReply(cookie, removeReply(&DatabaseInterface::removeAesMineArea, request)); } void HandlerMineArea::onMineResetRequest(const rti::core::SampleIdentity &cookie, const MineResetRequest &request) { DEBUG_LOG(m_logger) << "mineResetRequest: " << request; auto ret = mineResetRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->dds()->fts()->mineResetReply(cookie, ret); } Core::FunctionReplyObject HandlerMineArea::mineResetRequest(const MineResetRequest &request) { // If they are DUS Mines that are not part of a mine area [yet] // request does not contain position, so it cannot be assigned to an area if (request.mineAreaId() == 0) { // If single mine... if (request.mineId().is_set()) { DEBUG_LOG(m_logger) << "Checking in DUS-Mine-Areas"; bool found = false; auto it = m_objectMap.cbegin(); while (it != m_objectMap.cend() && !found) { found = (*it)->resetMine(request.mineId().get()); ++it; } if (!found) { DEBUG_LOG(m_logger) << "Not found in DUS Mine Areas, resetting in NullArea!"; m_nullArea->resetMine(request.mineId().get()); } } else { DEBUG_LOG(m_logger) << "Resetting all unassociated DUS mines (NullArea) and all DUS mine areas"; // Do this first! m_nullArea->resetMines(); for (auto mineAreaObj : qAsConst(m_objectMap)) { if (!mineAreaObj->isDusMineArea() && !mineAreaObj->isConcreteMine()) continue; mineAreaObj->resetMines(); } } return Core::FunctionReplyObject(request.mineAreaId()); } // Both Virtual and DUS Mine Area ObjectMineArea *ifs = m_objectMap.value(request.mineAreaId()); if (!ifs) return Core::FunctionReplyObject(request.mineAreaId(), tr_lts("dds", "Mine area with id %1 not found.")).arg(request.mineAreaId()); bool bOk; if (request.mineId().is_set()) bOk = ifs->resetMine(request.mineId().get()); else bOk = ifs->resetMines(); if (!bOk) return Core::FunctionReplyObject(request.mineAreaId(), tr_lts("dds", "Reset Virtual Mine(s) failed.")); return Core::FunctionReplyObject(request.mineAreaId()); } void HandlerMineArea::onMineTriggered(const TrainingAreaBase::MineTriggered &data, DataModification modType) { if (modType != DataModification::eAdded && modType != DataModification::eModified) return; MineTriggeredRequest mtr; mtr.mineAreaId(data.mineAreaId()); // from tab - mine area mine index starts at 1 ... mtr.mineId(data.mineAreaId() == 0 ? data.mineIndex() : data.mineIndex() - 1); if (data.position().is_set()) mtr.position(data.position().get()); if (data.chargeTypeId().is_set()) mtr.chargeTypeId(data.chargeTypeId().get()); auto ret = mineTriggeredRequest(mtr); if (ret.error()) WARNING_LOG(m_logger) << ret(); } void HandlerMineArea::onMineTriggeredRequest(const rti::core::SampleIdentity &cookie, const MineTriggeredRequest &request) { auto ret = mineTriggeredRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->dds()->fts()->mineTriggeredReply(cookie, ret); } Core::FunctionReplyObject HandlerMineArea::mineTriggeredRequest(const MineTriggeredRequest &request) { DEBUG_LOG(m_logger) << "mineTriggeredRequest: " << request; // NOTE: igniterType/IgniterFunciton = 0 as long as MineTriggeredRequest does not contain values if (request.mineAreaId() == 0 || (request.chargeTypeId().is_set() && isConcreteMine(request.chargeTypeId().get(), 0, 0))) { int chargeTypeId = request.chargeTypeId().is_set() ? request.chargeTypeId().get() : 0; // DUS Mines: // NOTE: igniterType/IgniterFunciton = 0 as long as MineTriggeredRequest does not contain values QPointF position; if (request.position().is_set() && !isFromMineLayer(chargeTypeId, 0, 0)) { position.setX(request.position().get().lon()); position.setY(request.position().get().lat()); } Database::AesMineAreaTriggeredMines mine; bool mineFound = false; // check if mine is already in a mine area and is still handled there auto it = m_objectMap.cbegin(); while (it != m_objectMap.cend() && !mineFound) { int mineId = request.mineId(); ObjectMineArea *mineArea = it.value(); DEBUG_LOG(m_logger) << "mineId: " << mineId << ", areaId:" << mineArea->id(); if (request.mineAreaId() > 0 && request.mineAreaId() != mineArea->id()) { it++; continue; } DEBUG_LOG(m_logger) << "request.mineAreaId: " << request.mineAreaId(); if (mineArea->isConcreteMine()) { DEBUG_LOG(m_logger) << "isConcrete = true"; mineId = mineArea->getMineIdByIndex(request.mineId()); } if ((mineArea->isDusMineArea() || mineArea->isConcreteMine()) && mineArea->hasMine(mineId)) // is mine in a DUS minearea { mineFound = true; DEBUG_LOG(m_logger) << "Mine found in Area " << mineArea->id(); if (mineArea->canHandleMine(mineId, chargeTypeId, position)) // is the mine area still handling the mine with the new position { DEBUG_LOG(m_logger) << "triggerMine: mineId: " << mineId; mineArea->triggerMine(mineId, true, position); return Core::FunctionReplyObject(request.mineAreaId()); } mine = mineArea->takeMine(mineId); // take the mine, as it is not handled anymore } ++it; } // search for a handling area (doesn't matter if new or existing mine at this point) int handlingAreaId = 0; it = m_objectMap.cbegin(); while (it != m_objectMap.cend() && handlingAreaId == 0) { const ObjectMineArea *mineArea = it.value(); if ((mineArea->isDusMineArea() || mineArea->isConcreteMine()) && mineArea->canHandleMine(request.mineId(), chargeTypeId, position)) handlingAreaId = mineArea->id(); ++it; } // if mine was not already in a mine area and there is an area that wants the mine, check if it can be taken from the null area if (!mineFound && handlingAreaId != 0) mine = m_nullArea->takeMine(request.mineId()); if (mine.id != 0) // mine did exist already { if (request.position().is_set()) // update position mine.position = position; } if (handlingAreaId != 0) { ObjectMineArea *ifs = m_objectMap.value(handlingAreaId); // if mine already exists -> move mine to handling mine area else just handle new mine if (mine.id != 0) { mine.isTriggered = true; ifs->updateMine(mine); } else { if (!triggerUnpairedMine(request)) ifs->triggerMine(request.mineId(), true, position); } } else { // if mine already exists -> move mine to null area else just handle new mine if (mine.id != 0) m_nullArea->triggerMine(mine); else { if (!triggerUnpairedMine(request)) m_nullArea->triggerMine(request.mineId(), true, chargeTypeId, position); } } return Core::FunctionReplyObject(request.mineAreaId()); } // Virtual: ObjectMineArea *ifs = m_objectMap.value(request.mineAreaId()); if (!ifs) return Core::FunctionReplyObject(request.mineAreaId(), tr_lts("dds", "Mine area with id %1 not found.")).arg(request.mineAreaId()); ifs->triggerMine(request.mineId()); return Core::FunctionReplyObject(request.mineAreaId()); } void HandlerMineArea::onMineAreaModifyFromCtaDataRequest(const rti::core::SampleIdentity &cookie, const MineArea &request) { DEBUG_LOG(m_logger) << "onMineAreaModifyRequest: " << request; const Core::FunctionReplyObject ret = mineAreaModifyRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->dds()->fts()->mineAreaModifyFromCtaDataReply(cookie, ret); } void HandlerMineArea::onNewMineCorridor(const AesItemView &aiv) { if (aiv.effectStateId() != AesEffectState::EsActivated) return; for (ObjectMineArea *ma : qAsConst(m_objectMap)) { if (intersects(ma->boundingRect(), aiv.form())) ma->clearCorridor(aiv); } } /*! * \brief HandlerMineArea::mineAreaReferenceCreateRequest * \warning this method is use for central mine only * \param refRequest * \return */ Core::FunctionReplyObject HandlerMineArea::mineAreaReferenceCreateRequest(const MineAreaReferenceModifyRequest &refRequest) { Core::FunctionReplyObject ret; if (refRequest.mineData().empty() || refRequest.referenceAesItemId() <= 0) return Core::FunctionReplyObject(1, tr_lts("dds", "Invalid MineAreaReference data")); DEBUG_LOG(m_logger) << "--[mineAreaReferenceCreateRequest] referenceAesItemId: " << refRequest.referenceAesItemId() << " mindeData size: " << refRequest.mineData().size(); Database::AesMineArea newData; if (!fillFromMineAreaReferenceRequest(refRequest, newData)) return Core::FunctionReplyObject(1, tr_lts("dds", "Invalid Referenced mine data")); // add referencedAesItemId into the properties QJsonObject propObj = QJsonDocument::fromJson(newData.properties.toUtf8()).object(); propObj.insert(QStringLiteral("referencedAesItemId"), refRequest.referenceAesItemId()); newData.properties = QString::fromStdString(QJsonDocument(propObj).toJson().toStdString()); ObjectMineArea *ifs(new ObjectMineArea(m_mainHandler, this)); std::vector vecGeoSegment; double newWidth = newData.width; // always central ret = ifs->createGeoSegmentsFromMineData(newWidth, refRequest.mineData(), vecGeoSegment); // segments will be filled through mineData if (ret.error()) { delete ifs; return ret; } newData.width = newWidth; connect(ifs, &ObjectMineArea::effectStateChanged, this, &HandlerMineArea::onEffectStateChanged); ret = ifs->createObjectFromMineData(newData, vecGeoSegment, refRequest.mineData()); if (ret.error()) { ERROR_LOG(m_logger) << "--[mineAreaReferenceCreateRequest] Couldn't create mine area !!!!"; delete ifs; return ret; } if (ifs->mineCount() > s_maxCentralSingleMinesCount) { ifs->removeObject(); // Not finally deleted from db after this,Fallback if DB deletion is not working correct ifs->removeObjectFromDb(); delete ifs; return Core::FunctionReplyObject(1, tr_lts("dds", "Too many central single mines.")); } connect(ifs, &ObjectMineArea::newAiv, this, &HandlerMineArea::newAiv); connect(ifs, &ObjectMineArea::removeAiv, this, &HandlerMineArea::removeAiv); ifs->clearBuildings(); m_objectMap.insert(ifs->id(), ifs); ++m_areaCountCentral; // is always central if (ifs->isObjectInTrainingArea()) ++m_areaCount; DEBUG_LOG(m_logger) << "--[mineAreaReferenceCreateRequest]- -----> Mine Area created from MineData with refAesItemId: " << refRequest.referenceAesItemId() << " ifs->id: " << ifs->id(); return ret; } /*! * \brief HandlerMineArea::mineAreaReferenceModifyRequest * \warning this method is use for central mine only * \param refRequest * \return */ Core::FunctionReplyObject HandlerMineArea::mineAreaReferenceModifyRequest(const MineAreaReferenceModifyRequest &refRequest) { DEBUG_LOG(m_logger) << "--[mineAreaReferenceModifyRequest]- Modify/Create Mine with refAesItemId: "; Core::FunctionReplyObject ret; ObjectMineArea *ifs = getObjecMapByreferenceAesItemId(refRequest.referenceAesItemId()); if (ifs == nullptr) { DEBUG_LOG(m_logger) << "--[mineAreaReferenceModifyRequest]- #### no Ref mine create one... refAesItemId: "<data()); if (!fillFromMineAreaReferenceRequest(refRequest, newData)) return Core::FunctionReplyObject(1, tr_lts("dds", "Invalid MineAreaReference data")); // create Geo Pos segments // reconstruct GeoPosSegment from (new)MineData + (existing)TriggerMineData std::vector vecGeoSegment; DEBUG_LOG(m_logger) << "--[mineAreaReferenceModifyRequest] create vector GeoPos from Mine data segments.. "; double newWidth = newData.width; ret = ifs->createGeoSegmentsFromMineData(newWidth, refRequest.mineData(), vecGeoSegment); if (ret.error()) return ret; newData.width = newWidth; double totalLength = 0.0; QVector vecDbSegments; DEBUG_LOG(m_logger) << "--[mineAreaReferenceModifyRequest] create vector DB MineArea segment from generated vec GeoPos.. "; ret = ifs->createSegments(vecGeoSegment, ifs->id(), vecDbSegments, totalLength); if (ret.error() || vecDbSegments.empty()) return ret; // run update ret = ifs->updateObjectFromMineData(newData, vecGeoSegment, refRequest.mineData()); ifs->clearBuildings(); DEBUG_LOG(m_logger) << "--[mineAreaReferenceModifyRequest] Mine Area Updated with MineData. AesItemId" << refRequest.referenceAesItemId() << " // data.id: " << ifs->id(); return ret; } void HandlerMineArea::onMineAreaReferenceModifyRequest(const rti::core::SampleIdentity &cookie, const MineAreaReferenceModifyRequest &request) { DEBUG_LOG(m_logger) << " onMineAreaReferenceModifyRequest: ADD MINE DATA with refAesItemId: " << request.referenceAesItemId(); const Core::FunctionReplyObject ret = mineAreaReferenceModifyRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->dds()->fts()->mineAreaReferenceModifyReply(cookie, ret); } /*! * \brief HandlerMineArea::fillFromMineAreaReferenceRequest * segments will not be filled from hier because they will be build (minedata + triggerMine) * \param inReq * \param outReq */ bool HandlerMineArea::fillFromMineAreaReferenceRequest(const MineAreaReferenceModifyRequest &inReq, Database::AesMineArea &dbData) { if (inReq.mineData().empty()) return false; const auto &mineData = std::min_element(inReq.mineData().begin(), inReq.mineData().end(), [](const MineData &first, const MineData &second) { return first.armingTime() < second.armingTime(); }); // get effect duration from chargeType properties const auto chargeType = m_mainHandler->dds()->pdmDtc()->chargeType(inReq.chargeTypeId()); const QJsonObject chargeTypePropObj = QJsonDocument::fromJson(chargeType.properties().c_str()).object(); const qint64 effectStart = mineData->armingTime(); const int effectDurationMsec = inReq.effectDurationMsec(); auto effectCluster = m_mainHandler->effectClusterHandler()->getEffectClusterObj(inReq.referenceAesItemId()); // get partyId from effect cluster object if (!effectCluster) { ERROR_LOG(m_logger) << " -- fillFromMineAreaReferenceRequest Unknown effect cluster id: " << inReq.referenceAesItemId(); return false; } double width = 250.0; // default double density = 1.0; // default bool effectMarkingTa = false; // default bool isLaidHidden = false; // default bool isCenterLine = true; // default AreaEffectSimulation::AesMaLayingProcedureType layingProcedure = AreaEffectSimulation::AesMaLayingProcedureType_def::LptPioneer; // get effectDuration from effectorData int id = 0; QString name = QStringLiteral("%1 %2"); ObjectMineArea *ifs = getObjecMapByreferenceAesItemId(inReq.referenceAesItemId()); if (ifs) // purpose is to get existing segments { id = ifs->id(); width = ifs->data().width; density = ifs->data().density; layingProcedure = ifs->data().layingProcedureTypeId; effectMarkingTa = ifs->data().effectMarkingInTa; isLaidHidden = ifs->data().isLaidHidden; isCenterLine = ifs->data().isCenterLine; name = ifs->data().name; } else { const auto chargeEffectChargeType = m_mainHandler->dds()->pdmDtc()->chargeType(effectCluster->data().chargeTypeId); name = name.arg(QString::fromStdString(chargeEffectChargeType.shortName())).arg(effectCluster->name()); } DEBUG_LOG(m_logger) << " Fill Mine from MineData effectCluster: id: " << effectCluster->data().id << " // effectClusterParty: " << effectCluster->data().partyId; dbData.id = id; dbData.name = name; dbData.effectStart = QDateTime::fromMSecsSinceEpoch(effectStart); dbData.effectDuration = effectDurationMsec; dbData.effectSourceId = effectCluster->data().effectSourceId; dbData.partyId = effectCluster->data().partyId; dbData.chargeTypeId = chargeType.id(); dbData.participantId = LtsUtils::toOptional(0); dbData.density = density; dbData.width = width; dbData.layingProcedureTypeId = layingProcedure; dbData.isLaidHidden = isLaidHidden; dbData.effectMarkingInTa = effectMarkingTa; dbData.isCenterLine = isCenterLine; dbData.externalId = LtsUtils::toOptional(0LL); dbData.isCentral = true; return true; } ObjectMineArea *HandlerMineArea::getObjecMapByreferenceAesItemId(int referenceAesItemId) { if (referenceAesItemId <= 0) return nullptr; std::function referenceIdFromProps = [](const std::string &props) -> int { if (props.empty()) return -1; const QJsonObject &propObj = QJsonDocument::fromJson(props.c_str()).object(); return propObj.value(QStringLiteral("referencedAesItemId")).toInt(-1); }; for (const auto &obj : m_objectMap) { if (referenceIdFromProps(obj->data().properties.toStdString()) == referenceAesItemId && obj->data().effectStateId != AreaEffectSimulation::AesEffectState::EsExpired && obj->data().effectStateId != AreaEffectSimulation::AesEffectState::EsRemoved && obj->data().effectStateId != AreaEffectSimulation::AesEffectState::EsError) { DEBUG_LOG(m_logger) << ">>>> found objectId: " << obj->data().id; return obj; } } return nullptr; // object with referenceAesItemId not found } void HandlerMineArea::assignMinesFromNullArea(int aesId) { ERROR_LOG(m_logger) << "Assign Mine State " << aesId; auto mineArea = m_objectMap.value(aesId); if (!mineArea->isDusMineArea() && !mineArea->isConcreteMine()) return; // check if there are already mines to assign from NullArea const QList mines = m_nullArea->mines(); for (const auto &mine : mines) { if (mineArea->canHandleMine(mine.mineId, mine.chargeTypeId, mine.position)) { auto nullMine = m_nullArea->takeMine(mine.mineId); mineArea->updateMine(nullMine); } } } void HandlerMineArea::onMinelayerPositionChanged(const TrainingAreaBase::MinelayerTrailerPosition &data, DataModification modType) { if (data.sampleTime() + 5000 < QDateTime::currentMSecsSinceEpoch()) { DEBUG_LOG(m_logger) << "ignore old layer data"; return; } if (modType == DataModification::eRemoved) return; DEBUG_LOG(m_logger) << "lat: " << data.position().lat() << ", lon: " << data.position().lon(); auto utm = LtsCoordTrans::UtmCoordinate::fromGeographic(data.position().lon(), data.position().lat()); QPointF position(utm.x, utm.y); QPointF positionGeo(data.position().lon(), data.position().lat()); m_zoneMap.insert(data.equipmentNumber(), utm.zone); DEBUG_LOG(m_logger) << "data." << data.equipmentNumber() << ", position: " << utm.mgrs(); if (m_timerMap.contains(data.equipmentNumber())) { QTimer *timer = m_timerMap.value(data.equipmentNumber()); if (timer->isActive()) { // Save Positions as QPolygon -> Vector not Map QPolygonF pointsUtm = m_dusMinePosMapUtm.value(data.equipmentNumber()); pointsUtm.append(position); m_dusMinePosMapUtm.insert(data.equipmentNumber(), pointsUtm); // Save Positions as QPolygon -> Vector not Map QPolygonF pointsGeo = m_dusMinePosMapGeo.value(data.equipmentNumber()); pointsGeo.append(positionGeo); m_dusMinePosMapGeo.insert(data.equipmentNumber(), pointsGeo); // m_dusMinePosMapUtm.insert(data.equipmentNumber(), position); // m_dusMinePosMapGeo.insert(data.equipmentNumber(), positionGeo); timer->start(); DEBUG_LOG(m_logger) << "Timer restart for equipNo: " << data.equipmentNumber(); } } else { DEBUG_LOG(m_logger) << "Start Timer for equipNo: " << data.equipmentNumber(); QTimer *timer = new QTimer(this); timer->setInterval(s_mineAreaCreationInterval); int equipNo = data.equipmentNumber(); int chargeTypeId = data.chargeTypeId(); connect(timer, &QTimer::timeout, this, [equipNo, chargeTypeId, this]() { ERROR_LOG(m_logger) << "Timeout for equipNo: " << equipNo; onTimeoutEquipNo(equipNo, chargeTypeId); }); QPolygonF pointUtm; pointUtm.append(position); m_dusMinePosMapUtm.insert(data.equipmentNumber(), pointUtm); QPolygonF pointGeo; pointGeo.append(positionGeo); m_dusMinePosMapGeo.insert(data.equipmentNumber(), pointGeo); // m_dusMinePosMapUtm.insert(data.equipmentNumber(), position); // m_dusMinePosMapGeo.insert(data.equipmentNumber(), positionGeo); timer->start(); m_timerMap.insert(equipNo, timer); } int dusSerialNo = data.dusSerialNo(); // DusMines with serialNo 0 should get a new serialNo ( > 70000), too // NOTE: igniterType/IgniterFunciton = 0 as long as MinelayerTrailerPosition does not contain values if (isConcreteMine(data.chargeTypeId(), 0, 0) || (isDusMineArea(data.chargeTypeId(), 0, 0) && dusSerialNo == 0)) dusSerialNo = ++s_startPzAbwWMiCCount; else { if (data.dusSerialNo() != 0 && removeDuplicateMine(data.dusSerialNo())) INFO_LOG(m_logger) << "Existing mine with serialNo: " << data.dusSerialNo() << " removed"; } DEBUG_LOG(m_logger) << "dusSerialNo: " << dusSerialNo; DEBUG_LOG(m_logger) << "isConcrete: " << isConcreteMine(data.chargeTypeId(), 0, 0); m_nullArea->triggerMine(dusSerialNo, false, data.chargeTypeId(), {data.position().lon(), data.position().lat()}); } void HandlerMineArea::onTimeoutEquipNo(int equipNo, int chargeTypeId) { QTimer *timer = m_timerMap.value(equipNo); timer->stop(); delete timer; m_timerMap.remove(equipNo); // NOTE: igniterType/IgniterFunciton = 0 as long as MinelayerTrailerPosition does not contain values if (!isDusMineArea(chargeTypeId, 0, 0) && !isConcreteMine(chargeTypeId, 0, 0)) return; DEBUG_LOG(m_logger) << "Timer for equipNo " << equipNo << " stopped"; QPolygonF points = m_dusMinePosMapGeo.value(equipNo); m_dusMinePosMapGeo.remove(equipNo); m_dusMinePosMapUtm.remove(equipNo); int mineCount = points.size(); TRACE_LOG(m_logger) << "mineCount: " << mineCount; // QPolygonF boundingBox = msGetBoundingBoxPolygon(points, &width); MineAreaModifyRequest req; req.chargeStruct().chargeTypeId(chargeTypeId); req.density(0.7); req.distance(0.5); DEBUG_LOG(m_logger) << "density1: " << req.density(); req.effectDuration(0); req.effectStart(QDateTime::currentMSecsSinceEpoch() + Settings::getMineAreaByMiVSysEffectStart()); req.isCenterLine(true); req.isLaidHidden(false); req.layingProcedureType(AesMaLayingProcedureType_def::LptPioneer); QString name = QString::number(equipNo); req.name(name.toStdString()); int participantID = m_mainHandler->dds()->ppmDtc()->participantIdByEquipmentNumber(equipNo); req.partyId(m_mainHandler->dds()->ppmDtc()->participant(participantID).partyId()); req.width(0); std::vector vec; vec.reserve(points.size()); for (QPointF bbPoint : qAsConst(points)) { Core::GeoPosition2D pos; pos.lon(bbPoint.x()); pos.lat(bbPoint.y()); vec.push_back(pos); } // vec.reserve(boundingBox.size()); // for( QPointF bbPoint : qAsConst(boundingBox)) // { // Core::GeoPosition2D pos; // LtsCoordTrans::UtmCoordinate utm; // utm.x = bbPoint.x(); // utm.y = bbPoint.y(); // utm.zone = m_zoneMap.value(equipNo); // QPointF point = utm.geographic(); // DEBUG_LOG(m_logger) << "position: " << utm.mgrs(); // pos.lon(point.x()); // pos.lat(point.y()); // vec.push_back(pos); // } req.segments(vec); // Cannot create a minefield with only one segment => create two new segments with 10m distance if (req.segments().size() < 2) { DEBUG_LOG(m_logger) << "only one segment - create 2 new with distance 10m"; const LtsCoordTrans::UtmCoordinate pointUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(vec[0].lon(), vec[0].lat()); LtsCoordTrans::UtmCoordinate point1Utm = pointUtm; LtsCoordTrans::UtmCoordinate point2Utm = pointUtm; point1Utm.y = pointUtm.y - 5.0; point2Utm.y = pointUtm.y + 5.0; vec.clear(); vec.reserve(2); QPointF point = point1Utm.geographic(); Core::GeoPosition2D pos(point.x(), point.y()); vec.push_back(pos); point = point2Utm.geographic(); pos.lon(point.x()); pos.lat(point.y()); vec.push_back(pos); req.segments(vec); req.distance(10.0); } Core::FunctionReplyObject reply = mineAreaCreateRequest(req); if (reply.error()) ERROR_LOG(m_logger) << "Could not create mineArea from dus mines: " << reply(); else DEBUG_LOG(m_logger) << "MineArea created with " << mineCount << " mines"; } bool HandlerMineArea::checkIfNullMineIsInArea(const std::vector &segments, const Database::AesMineArea &data, double distance) { QVector vecSeg; vecSeg.reserve(segments.size()); if (segments.size() == 0) return false; for (unsigned int i = 0; i < segments.size() - 1; i++) { Database::AesMineAreaSegment dataSeg; dataSeg.startPosition = QPointF(segments[i].lon(), segments[i].lat()); dataSeg.endPosition = QPointF(segments[i + 1].lon(), segments[i + 1].lat()); vecSeg.append(dataSeg); } return checkIfNullMineIsInArea(vecSeg, data, distance); } bool HandlerMineArea::checkIfNullMineIsInArea(const std::vector &segments, const Database::AesMineArea &data, double distance) { QVector vecSeg; vecSeg.reserve(segments.size()); for (const AreaEffectSimulation::MineSegment &seg : qAsConst(segments)) { Database::AesMineAreaSegment dbSeg; dbSeg.startPosition = QPointF(seg.positionStart().lon(), seg.positionStart().lat()); dbSeg.endPosition = QPointF(seg.positionEnd().lon(), seg.positionEnd().lat()); vecSeg.append(dbSeg); } return checkIfNullMineIsInArea(vecSeg, data, distance); } bool HandlerMineArea::checkIfNullMineIsInArea(const QVector &segments, const Database::AesMineArea &data, double distance) { QPolygonF poly = Polygons::mineArea(segments, data.width, data.density, data.isCenterLine); const QList mines = m_nullArea->mines(); DEBUG_LOG(m_logger) << "Mines in null area: " << mines.size(); for (const Database::AesMineAreaTriggeredMines &mine : mines) { if (containsPointInExpandedMinefield(mine.position, poly, distance)) return true; } return false; } bool HandlerMineArea::containsPointInExpandedMinefield(const QPointF &positionGeo, const QPolygonF &polygon, double distance) const { QPolygonF mineAreaPolygonUtm; mineAreaPolygonUtm.reserve(polygon.size()); const LtsCoordTrans::UtmCoordinate pointUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(positionGeo.x(), positionGeo.y()); DEBUG_LOG(m_logger) << "centerPoint=" << pointUtm.x << "/" << pointUtm.y << " zone=" << pointUtm.zone << "- mgrs: " << pointUtm.mgrs(); for (const auto &pos : polygon) { const LtsCoordTrans::UtmCoordinate &pointMineAreaUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(pos, pointUtm.zone); DEBUG_LOG(m_logger) << "pointMineAreaUtm=" << pointMineAreaUtm.x << "/" << pointMineAreaUtm.y << " zone=" << pointMineAreaUtm.zone << " - mgrs: " << pointMineAreaUtm.mgrs(); mineAreaPolygonUtm.push_back(QPointF(pointMineAreaUtm.x, pointMineAreaUtm.y)); } for (const QPointF &mineAreaPointUTM : qAsConst(mineAreaPolygonUtm)) { double x = pointUtm.x - mineAreaPointUTM.x(); double y = pointUtm.y - mineAreaPointUTM.y(); double distancePointPointValue = sqrt(x * x + y * y); DEBUG_LOG(m_logger) << "distancePointPointValue=" << distancePointPointValue << " Point =" << pointUtm.x << "/" << pointUtm.y << " PointPoly=" << mineAreaPointUTM.x() << "/" << mineAreaPointUTM.y(); if (distancePointPointValue < distance) { DEBUG_LOG(m_logger) << "HandlerMineArea::containsPoint return true"; return true; } } return false; } double HandlerMineArea::msGetDistanceFromLine(float px, float py, float px0, float py0, float px1, float py1) { double dist; double a11; double a12; double a21; double a22; double b1; double b2; double det; double x; a11 = px1 - px0; a12 = py1 - py0; a21 = py1 - py0; a22 = px0 - px1; b1 = px - px0; b2 = py - py0; det = a11 * a22 - a12 * a21; if (fabs(det) < 0.001) return 0.0; x = (a11 * b2 - b1 * a21) / det; dist = x * sqrt(a21 * a21 + a22 * a22); return dist; } QPolygonF HandlerMineArea::msGetBoundingBoxPolygon(const QPolygonF &minen, double *widthBoundingBox) { QPolygonF bb; double dist; double distMax; double width; double width_minus; double width_plus; double x; double y; double x0; double y0; double x1; double y1; double vx; double vy; double px0; double py0; double px1; double py1; double bbv; int idxPolygon0; int idxPolygon1; distMax = 0.0; idxPolygon0 = 0; idxPolygon1 = 1; for (int i = 0; i < minen.size(); i++) { for (int j = i + 1; j < minen.size(); j++) { x0 = minen.toPolygon().at(i).x(); y0 = minen.toPolygon().at(i).y(); x1 = minen.toPolygon().at(j).x(); y1 = minen.toPolygon().at(j).y(); dist = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); if (dist > distMax) { distMax = dist; idxPolygon0 = i; idxPolygon1 = j; } } } bbv = 5.0; if (distMax < 0.001) { x0 = minen.toPolygon().at(idxPolygon0).x(); y0 = minen.toPolygon().at(idxPolygon0).y(); bb.append(QPointF(x0 - 0.71 * bbv, y0 - 0.71 * bbv)); bb.append(QPointF(x0 + 0.71 * bbv, y0 + 0.71 * bbv)); *widthBoundingBox = 2 * bbv; return bb; } vx = (minen.toPolygon().at(idxPolygon1).x() - minen.toPolygon().at(idxPolygon0).x()) / distMax; vy = (minen.toPolygon().at(idxPolygon1).y() - minen.toPolygon().at(idxPolygon0).y()) / distMax; width_minus = 0.0; width_plus = 0.0; for (int i = 0; i < minen.size(); i++) { if (i != idxPolygon0 && i != idxPolygon1) { x = minen.toPolygon().at(i).x(); y = minen.toPolygon().at(i).y(); x0 = minen.toPolygon().at(idxPolygon0).x(); y0 = minen.toPolygon().at(idxPolygon0).y(); x1 = minen.toPolygon().at(idxPolygon1).x(); y1 = minen.toPolygon().at(idxPolygon1).y(); width = msGetDistanceFromLine(x, y, x0, y0, x1, y1); if (width < width_minus) width_minus = width; if (width > width_plus) width_plus = width; } } width = (width_plus + width_minus) / 2.0; px0 = minen.toPolygon().at(idxPolygon0).x() + vy * width - vx * bbv; py0 = minen.toPolygon().at(idxPolygon0).y() - vx * width - vy * bbv; bb.append(QPointF(px0, py0)); px1 = minen.toPolygon().at(idxPolygon1).x() + vy * width + vx * bbv; py1 = minen.toPolygon().at(idxPolygon1).y() - vx * width + vy * bbv; bb.append(QPointF(px1, py1)); *widthBoundingBox = width_plus - width_minus + 2 * bbv; return bb; } bool HandlerMineArea::triggerUnpairedMine(const MineTriggeredRequest &request) { if (!request.position().is_set()) { DEBUG_LOG(m_logger) << LOGFUNCTION << " failed, because there is no position."; return false; } qint32 halfLookupDistance = Settings::getUnpairedMinesLookupRange() / 2; LtsCoordTrans::UtmCoordinate mineUtmPos = LtsCoordTrans::UtmCoordinate::fromGeographic(request.position().get().lon(), request.position().get().lat()); QPointF lookupTopLeft = mineUtmPos.geographic(-halfLookupDistance, -halfLookupDistance); QPointF lookupBottomRight = mineUtmPos.geographic(halfLookupDistance, halfLookupDistance); QRectF lookupRect = QRectF(lookupTopLeft, lookupBottomRight); Database::AesMineAreaTriggeredMines nearestMine; bool hasUnpairedMine = false; qreal shortestDistance = std::numeric_limits::max(); QPointF positionPoint; for (auto it = m_objectMap.constBegin(); it != m_objectMap.constEnd(); ++it) { if (it.value()->isConcreteMine() || !it.value()->boundingRect().intersects(lookupRect)) continue; const QVector unpairedMines = it.value()->unpairedMines(); for (const Database::AesMineAreaTriggeredMines ¤tMine : unpairedMines) { positionPoint.setX(request.position().get().lon()); positionPoint.setY(request.position().get().lat()); qreal currentDistance = LtsCoordTrans::distance(positionPoint, currentMine.position); if (currentDistance >= shortestDistance) continue; shortestDistance = currentDistance; nearestMine = currentMine; hasUnpairedMine = true; } } if (!hasUnpairedMine) { DEBUG_LOG(m_logger) << LOGFUNCTION << " failed, because there is no unpaired mine in range."; return false; } ObjectMineArea *ifs = m_objectMap.value(nearestMine.mineAreaId); qint32 oldMineId = nearestMine.mineId; nearestMine.mineId = request.mineId(); nearestMine.isTriggered = true; ifs->updateMineByOldMineId(nearestMine, oldMineId); DEBUG_LOG(m_logger) << LOGFUNCTION << " was successful."; return true; } } // Namespace AreaEffectSimulation