Files
qt5-legatus-legacy/Lts/Services/AreaEffectSimulation/handler_mine_area.cpp
T
2026-08-13 18:33:10 +02:00

1507 lines
55 KiB
C++
Executable File

/**
* 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 <QPainterPath>
#include <QPolygonF>
#include <QSet>
#include <TrainingAreaBase/tab_dtc.h>
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
namespace AreaEffectSimulation
{
HandlerMineArea::HandlerMineArea(MainHandler *mainHandler)
: HandlerBaseTmpl<ObjectMineArea>(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<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(chargeTypeId, igniterTypeId, igniterFunctionId)) == MineClass::DUS;
}
bool HandlerMineArea::isConcreteMine(int chargeTypeId, int igniterTypeId, int igniterFunctionId) const
{
return static_cast<MineClass::type>(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<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(chargeTypeId, igniterTypeId, igniterFunctionId)) == MineClass::VirtualSingle;
}
bool HandlerMineArea::isVirtualMine(int chargeTypeId, int igniterTypeId, int igniterFunctionId) const
{
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(chargeTypeId, igniterTypeId, igniterFunctionId)) == MineClass::Virtual;
}
bool HandlerMineArea::init()
{
QVector<Database::AesMineArea> 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<Database::AesMineAreaTriggeredMines> 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<Database::AesMineAreaTriggeredMines> 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 <typename T> T getValueHelper(T in) { return in; }
template <typename T> T getValueHelper(const dds::core::optional<T> &in) { return in.is_set() ? in.get() : T(); }
template <typename T> 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<Core::GeoPosition> &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 <typename T> 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<Core::GeoPosition> _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 <typename T> 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<Database::AesMineAreaSegment> 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<Core::GeoPosition> _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<Core::GeoPosition2D> 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: "<<refRequest.referenceAesItemId();
return mineAreaReferenceCreateRequest(refRequest);
}
Database::AesMineArea newData(ifs->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<Core::GeoPosition2D> 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<Database::AesMineAreaSegment> 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<int(std::string)> 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<Database::AesMineAreaTriggeredMines> 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<Core::GeoPosition2D> 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<Core::GeoPosition2D> &segments, const Database::AesMineArea &data, double distance)
{
QVector<Database::AesMineAreaSegment> 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<AreaEffectSimulation::MineSegment> &segments, const Database::AesMineArea &data, double distance)
{
QVector<Database::AesMineAreaSegment> 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<Database::AesMineAreaSegment> &segments, const Database::AesMineArea &data, double distance)
{
QPolygonF poly = Polygons::mineArea(segments, data.width, data.density, data.isCenterLine);
const QList<Database::AesMineAreaTriggeredMines> 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<qreal>::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<Database::AesMineAreaTriggeredMines> unpairedMines = it.value()->unpairedMines();
for (const Database::AesMineAreaTriggeredMines &currentMine : 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