1662 lines
62 KiB
C++
Executable File
1662 lines
62 KiB
C++
Executable File
/**
|
|
* Handler for the HMI language
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
#include "object_mine_area.h"
|
|
#include "handler_mine_area.h"
|
|
#include "qtransform.h"
|
|
#include "special_area_provider.h"
|
|
|
|
#include "Utils/polygons.h"
|
|
|
|
#include <AreaEffectSimulation/aes_translation.h>
|
|
|
|
#include <QLineF>
|
|
#include <QPolygonF>
|
|
#include <LtsUtils/LtsUtilsGui/LtsUtilsGuiDds/request_helper.h> // LtsUtils::toOptional
|
|
#include <LtsUtilsCore/dds_bitarray.h>
|
|
#include <ngldb/dbtransaction.h>
|
|
|
|
namespace AreaEffectSimulation
|
|
{
|
|
namespace Database
|
|
{
|
|
bool operator==(const AesMineAreaSegment &a, const AesMineAreaSegment &b)
|
|
{
|
|
return ((a.id == b.id) && (a.mineAreaId == b.mineAreaId) && (a.elementNumber == b.elementNumber) && (a.endPosition == b.endPosition)
|
|
&& (a.startPosition == b.startPosition) && (a.endPosition == b.endPosition));
|
|
}
|
|
} // namespace Database
|
|
|
|
ObjectMineArea::ObjectMineArea(MainHandler *mainHandler, QObject *parent)
|
|
: ObjectBaseTmpl(mainHandler, QStringLiteral("MineArea"), parent)
|
|
, m_triggeredMinesChanged(false)
|
|
{
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::ObjectMineArea";
|
|
connect(this, &ObjectMineArea::effectStateChanged, this, &ObjectMineArea::onEffectStateChanged);
|
|
}
|
|
|
|
ObjectMineArea::~ObjectMineArea()
|
|
{
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::~ObjectMineArea";
|
|
removeTopics();
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::createObject(const Database::AesMineArea &data, const std::vector<Core::GeoPosition2D> &segments)
|
|
{
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::createObject id=" << data.id << " externalId=" << data.externalId << " isCentral=" << data.isCentral
|
|
<< " chargeTypeId=" << data.chargeTypeId << " width=" << data.width << " segments.size()=" << segments.size();
|
|
return createObjectTmpl(data, segments);
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::createObject(const Database::AesMineArea &data, const std::vector<MineSegment> &segments)
|
|
{
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::createObject id=" << data.id << " segments.size()=" << segments.size();
|
|
return createObjectTmpl(data, segments);
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::updateObject(const Database::AesMineArea &data, const std::vector<Core::GeoPosition2D> &segments)
|
|
{
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::updateObject id=" << data.id << " segments.size=" << segments.size();
|
|
return updateObjectTmpl(data, segments);
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::updateObject(const Database::AesMineArea &data, const std::vector<MineSegment> &segments)
|
|
{
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::updateObject id=" << data.id << " segments.size=" << segments.size();
|
|
return updateObjectTmpl(data, segments);
|
|
}
|
|
|
|
bool ObjectMineArea::removeObjectFromDb() { return m_mainHandler->db()->removeAesMineArea(m_data.id); }
|
|
|
|
bool ObjectMineArea::isDusMineArea() const
|
|
{
|
|
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
|
|
== MineClass::DUS;
|
|
}
|
|
|
|
bool ObjectMineArea::isConcreteMine() const
|
|
{
|
|
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
|
|
== MineClass::Concrete;
|
|
}
|
|
|
|
bool ObjectMineArea::isSingleVirtual() const
|
|
{
|
|
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
|
|
== MineClass::VirtualSingle;
|
|
}
|
|
|
|
bool ObjectMineArea::isVirtualMine() const
|
|
{
|
|
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
|
|
== MineClass::Virtual;
|
|
}
|
|
|
|
int ObjectMineArea::lastMineNo() const
|
|
{
|
|
int lastMineNoResult = 0;
|
|
if (m_triggeredMines.isEmpty())
|
|
{
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::lastMineNo=" << 0;
|
|
return 0;
|
|
}
|
|
lastMineNoResult = m_triggeredMines.lastKey();
|
|
DEBUG_LOG(m_logger) << "ObjectMineArea::lastMineNo=" << lastMineNoResult;
|
|
return lastMineNoResult;
|
|
}
|
|
|
|
int ObjectMineArea::getMineIdByIndex(int index) const
|
|
{
|
|
if (m_triggeredMines.size() < index)
|
|
return -1;
|
|
|
|
DEBUG_LOG(m_logger) << "getMineIdByIndex index: " << index;
|
|
int count = 0;
|
|
for (const Database::AesMineAreaTriggeredMines &tm : qAsConst(m_triggeredMines))
|
|
{
|
|
DEBUG_LOG(m_logger) << "tm: mineId: " << tm.mineId;
|
|
if (count == index)
|
|
return tm.mineId;
|
|
|
|
count++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
bool ObjectMineArea::isReactivatableDusMineArea() const
|
|
{
|
|
bool isReactivatableDusMineAreaResult = m_mainHandler->dds()->pdmDtc()->isReactivatable(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId);
|
|
DEBUG_LOG(m_logger) << "isReactivatableDusMineArea=" << isReactivatableDusMineAreaResult;
|
|
return isReactivatableDusMineAreaResult;
|
|
}
|
|
|
|
void ObjectMineArea::clearCorridor(const AesItemView &aiv)
|
|
{
|
|
if (effectState() != AesEffectState::EsActivated)
|
|
return;
|
|
|
|
QPolygonF aivUtmPoly;
|
|
aivUtmPoly.reserve(aiv.form().size());
|
|
int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(aiv.form()[0].lon(), aiv.form()[0].lat()).zone;
|
|
for (const auto &pos : aiv.form()) // Last Pos just for Length and Direction...
|
|
{
|
|
LtsCoordTrans::UtmCoordinate utmPos = LtsCoordTrans::UtmCoordinate::fromGeographic(pos.lon(), pos.lat(), zone);
|
|
aivUtmPoly.push_back(QPointF(utmPos.x, utmPos.y));
|
|
}
|
|
|
|
QVector<int> minesToTrigger;
|
|
minesToTrigger.reserve(m_triggeredMines.size());
|
|
for (const Database::AesMineAreaTriggeredMines &tm : qAsConst(m_triggeredMines))
|
|
{
|
|
LtsCoordTrans::UtmCoordinate utmMinePos = LtsCoordTrans::UtmCoordinate::fromGeographic(tm.position, zone);
|
|
if (aivUtmPoly.containsPoint(QPointF(utmMinePos.x, utmMinePos.y), Qt::OddEvenFill))
|
|
minesToTrigger << tm.mineId;
|
|
}
|
|
|
|
triggerMines(minesToTrigger, true, QPointF());
|
|
}
|
|
|
|
void ObjectMineArea::clearBuildings()
|
|
{
|
|
int zoneOut = -1;
|
|
// Check if buildings intersect with mine area
|
|
|
|
bool isGroundLevel = true;
|
|
const QVector<int> &mseIds = m_mainHandler->dds()->ismDtc()->getBuildingByPolygon(m_mainHandler->dds()->exerciseId(), m_mineAreaPolygon, false, isGroundLevel);
|
|
|
|
DEBUG_LOG(m_logger) << "mseIds size: " << mseIds.size();
|
|
QVector<int> minesToTrigger;
|
|
for (int mseId : qAsConst(mseIds))
|
|
{
|
|
DEBUG_LOG(m_logger) << "mseId: " << mseId;
|
|
const QPolygonF &footprint = m_mainHandler->dds()->ismDtc()->getUtmFootprintById(mseId, zoneOut, isGroundLevel);
|
|
|
|
minesToTrigger.clear();
|
|
minesToTrigger.reserve(m_triggeredMines.size());
|
|
for (const Database::AesMineAreaTriggeredMines &tm : qAsConst(m_triggeredMines))
|
|
{
|
|
const LtsCoordTrans::UtmCoordinate &utmMinePos = LtsCoordTrans::UtmCoordinate::fromGeographic(tm.position, zoneOut);
|
|
if (footprint.containsPoint(QPointF(utmMinePos.x, utmMinePos.y), Qt::OddEvenFill))
|
|
minesToTrigger << tm.mineId;
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "minesToTrigger: " << minesToTrigger.size();
|
|
removeTriggeredMines(minesToTrigger);
|
|
}
|
|
}
|
|
|
|
bool ObjectMineArea::removeTriggeredMines(const QVector<int> &mineIds)
|
|
{
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
for (int mineId : mineIds)
|
|
{
|
|
Database::AesMineAreaTriggeredMines &tm = m_triggeredMines[mineId];
|
|
DEBUG_LOG(m_logger) << "remove triggered Mine " << mineId;
|
|
bool bOk = m_mainHandler->db()->removeAesMineAreaTriggeredMines(tm.id);
|
|
|
|
if (!bOk)
|
|
{
|
|
ERROR_LOG(m_logger) << "Error removing mine " << tm.mineId;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// remove from m_triggeredMines after suscessful removed from db
|
|
for (int mineId : mineIds)
|
|
m_triggeredMines.remove(mineId);
|
|
|
|
if (!isDusMineArea())
|
|
{
|
|
// This not nice.
|
|
QList<Database::AesMineAreaTriggeredMines> values = m_triggeredMines.values();
|
|
m_triggeredMines.clear();
|
|
bool bOk = true;
|
|
for (int i = 0; i < values.size(); ++i)
|
|
{
|
|
values[i].mineId = i;
|
|
m_triggeredMines[i] = values[i];
|
|
bOk &= m_mainHandler->db()->update(values[i]);
|
|
}
|
|
if (!bOk)
|
|
{
|
|
ERROR_LOG(m_logger) << "Error updating at least one mine ";
|
|
return false;
|
|
}
|
|
}
|
|
trans.commit();
|
|
|
|
m_triggeredMinesChanged = true;
|
|
onPublishData();
|
|
|
|
return true;
|
|
}
|
|
|
|
QVector<Database::AesMineAreaTriggeredMines> ObjectMineArea::unpairedMines() const
|
|
{
|
|
QVector<Database::AesMineAreaTriggeredMines> result;
|
|
|
|
for (auto it = m_triggeredMines.constBegin(); it != m_triggeredMines.constEnd(); ++it)
|
|
{
|
|
if (it.value().mineId < 70000 || it.value().isTriggered)
|
|
continue;
|
|
|
|
result.append(it.value());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::createSegments(const std::vector<Core::GeoPosition2D> &inSegments,
|
|
int mineAreaId,
|
|
QVector<Database::AesMineAreaSegment> &outSegments,
|
|
double &totalLengthOut)
|
|
{
|
|
DEBUG_LOG(m_logger) << "createDbSegments inSegments.size=" << inSegments.size();
|
|
outSegments.clear();
|
|
|
|
if (inSegments.size() < 2)
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "There have to be at least two points to create a Mine Area Segment."));
|
|
|
|
int count = 0;
|
|
outSegments.reserve(inSegments.size());
|
|
LtsCoordTrans::UtmCoordinate start = LtsCoordTrans::UtmCoordinate::fromGeographic(inSegments[0].lon(), inSegments[0].lat());
|
|
int zone = start.zone;
|
|
totalLengthOut = 0.0;
|
|
for (size_t i = 0; i < inSegments.size() - 1; ++i) // Last Pos just for Length and Direction...
|
|
{
|
|
Database::AesMineAreaSegment newSegment;
|
|
newSegment.elementNumber = ++count;
|
|
newSegment.mineAreaId = mineAreaId;
|
|
newSegment.id = 0;
|
|
newSegment.startPosition = QPointF(inSegments[i].lon(), inSegments[i].lat());
|
|
newSegment.endPosition = QPointF(inSegments[i + 1].lon(), inSegments[i + 1].lat());
|
|
// calculate length
|
|
const LtsCoordTrans::UtmCoordinate end = LtsCoordTrans::UtmCoordinate::fromGeographic(inSegments[i + 1].lon(), inSegments[i + 1].lat(), zone);
|
|
// QLine's y-axis is southbound!
|
|
QLineF line(QPointF(start.x, start.y), QPointF(end.x, end.y));
|
|
// if (line.length() * density < 0.95) // We top that up to one mine later on.
|
|
// {
|
|
// outSegments.clear();
|
|
// return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Distance between Point %1 and Point %2 is too short to fit at least one mine.")).arg(i + 1).arg(i +
|
|
// 2);
|
|
// }
|
|
start = end;
|
|
|
|
// Misuse mineCount as SegmentLength in [m] ?
|
|
// newSegment.mineCount = qRound(line.length());
|
|
totalLengthOut += line.length();
|
|
outSegments.push_back(std::move(newSegment));
|
|
}
|
|
return Core::FunctionReplyObject(m_data.id);
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::checkDensity(double &densityOut, const double totalLength, int maxMineCount)
|
|
{
|
|
DEBUG_LOG(m_logger) << "density: " << densityOut << " totalLength: " << totalLength;
|
|
if (densityOut * totalLength > maxMineCount) // = MineCount...
|
|
densityOut = maxMineCount / totalLength;
|
|
DEBUG_LOG(m_logger) << "new density: " << densityOut;
|
|
|
|
if (densityOut < 0.01)
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Calculated density is too low for the given length"));
|
|
if (densityOut > 0.9)
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Calculated density is too high for the given length"));
|
|
|
|
return Core::FunctionReplyObject(m_data.id);
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::createSegments(
|
|
const std::vector<AreaEffectSimulation::MineSegment> &inSegments,
|
|
int mineAreaId,
|
|
QVector<Database::AesMineAreaSegment> &outSegments,
|
|
const double &totalLengthOut // const double &totalLengthOut since totalLengthOut is unused and exits just for signature purpose
|
|
)
|
|
{
|
|
Q_UNUSED(totalLengthOut)
|
|
DEBUG_LOG(m_logger) << "createSegments inSegments.size=" << inSegments.size();
|
|
outSegments.clear();
|
|
|
|
const size_t newSegmentsSize = inSegments.size();
|
|
outSegments.reserve(newSegmentsSize);
|
|
|
|
for (size_t i = 0; i < newSegmentsSize; ++i)
|
|
{
|
|
Database::AesMineAreaSegment dbSegment;
|
|
const AreaEffectSimulation::MineSegment &segment = inSegments.at(i);
|
|
|
|
dbSegment.elementNumber = i;
|
|
dbSegment.startPosition = QPointF(segment.positionStart().lon(), segment.positionStart().lat());
|
|
dbSegment.endPosition = QPointF(segment.positionEnd().lon(), segment.positionEnd().lat());
|
|
dbSegment.mineAreaId = mineAreaId;
|
|
|
|
outSegments.push_back(std::move(dbSegment));
|
|
}
|
|
|
|
return Core::FunctionReplyObject(m_data.id);
|
|
}
|
|
|
|
bool ObjectMineArea::publishTriggeredImpacts()
|
|
{
|
|
DEBUG_LOG(m_logger) << "publishTriggeredImpacts";
|
|
// Build an own tes-bitfield and compare, only store and publish if changed.
|
|
TriggeredImpacts tis(m_tis);
|
|
|
|
tis.impactCount(m_triggeredMines.size());
|
|
tis.bitfield().resize((m_triggeredMines.size() + 7) / 8);
|
|
|
|
int i = 0;
|
|
for (const auto &mine : qAsConst(m_triggeredMines))
|
|
{
|
|
// Triggered Impact == 0, if mine is disarmed/exploded
|
|
LtsUtils::BitArray::setBit(tis.bitfield(), i++, !mine.isTriggered);
|
|
}
|
|
|
|
if ((m_tis != tis) || m_triggeredMinesChanged)
|
|
{
|
|
m_tis = tis;
|
|
m_tis.sampleTime(partitionTimeMSec());
|
|
return m_mainHandler->dds()->dts()->publish(m_tis);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ObjectMineArea::publishDusMineStatus()
|
|
{
|
|
DEBUG_LOG(m_logger) << "publishDusMineStatus";
|
|
// DusMineStatus mines vector is coherent to triggered impacts bitfield
|
|
DusMineState dms(m_dms);
|
|
|
|
dms.mines().resize(m_triggeredMines.size());
|
|
int i = 0;
|
|
|
|
for (const auto &mine : qAsConst(m_triggeredMines))
|
|
{
|
|
const DusMine entry(mine.mineId, mine.isTriggered, mine.chargeTypeId, {mine.position.x(), mine.position.y()});
|
|
dms.mines()[i++] = entry;
|
|
}
|
|
|
|
if (m_dms != dms || m_triggeredMinesChanged)
|
|
{
|
|
m_dms = dms;
|
|
m_dms.sampleTime(partitionTimeMSec());
|
|
return m_mainHandler->dds()->dts()->publish(m_dms);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Core::FunctionReplyObject ObjectMineArea::changeSegmentsToLine(bool sortMines)
|
|
{
|
|
if (m_triggeredMines.size() < 1)
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Could not change mine area segments with no mines."));
|
|
|
|
QVector<Core::GeoPosition2D> vec;
|
|
vec.reserve(m_triggeredMines.size());
|
|
|
|
QVector<LtsCoordTrans::UtmCoordinate> polyUtm;
|
|
polyUtm.reserve(m_mineAreaPolygon.size());
|
|
|
|
for (const QPointF &point : qAsConst(m_mineAreaPolygon))
|
|
{
|
|
LtsCoordTrans::UtmCoordinate posUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(point.x(), point.y());
|
|
DEBUG_LOG(m_logger) << "mineArea Polygon: " << posUtm.mgrs();
|
|
polyUtm.append(posUtm);
|
|
}
|
|
|
|
QVector<LtsCoordTrans::UtmCoordinate> minesUtm;
|
|
minesUtm.reserve(m_triggeredMines.size());
|
|
for (const Database::AesMineAreaTriggeredMines &tm : qAsConst(m_triggeredMines))
|
|
{
|
|
LtsCoordTrans::UtmCoordinate posUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(tm.position.x(), tm.position.y());
|
|
DEBUG_LOG(m_logger) << "triggeredMines: " << posUtm.mgrs();
|
|
minesUtm.append(posUtm);
|
|
}
|
|
|
|
for (const Database::AesMineAreaTriggeredMines &tm : qAsConst(m_triggeredMines))
|
|
{
|
|
Core::GeoPosition2D geoPos = Core::GeoPosition2D(tm.position.x(), tm.position.y());
|
|
vec.append(geoPos);
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "vec.size: " << vec.size();
|
|
|
|
std::vector<Core::GeoPosition2D> vecSorted;
|
|
if (!sortMines)
|
|
{
|
|
// In case of Concrete Mines and a DUS Line of only 2 Mines there is no need for any change
|
|
if (vec.size() < 3 || isConcreteMine())
|
|
vecSorted = std::vector<Core::GeoPosition2D>(vec.begin(), vec.end());
|
|
else
|
|
{
|
|
// In case of MineLayerPoisition (Not Sorting) with DUS Mines and more than 2 Mines (1 Segment) we
|
|
// ignore the mines and just use the segments positions (Start is the startingPos of the the first segment
|
|
// then add all end Positions of all segments
|
|
for (const auto &segment : qAsConst(m_segments))
|
|
{
|
|
if (vecSorted.empty())
|
|
vecSorted.push_back(Core::GeoPosition2D(segment.startPosition.x(), segment.startPosition.y()));
|
|
vecSorted.push_back(Core::GeoPosition2D(segment.endPosition.x(), segment.endPosition.y()));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// get startPos of all mines (nearest to the smaller side of the polygon)
|
|
int index = msGetIndexMine(polyUtm, minesUtm);
|
|
DEBUG_LOG(m_logger) << "index: " << index;
|
|
Core::GeoPosition2D startPos = vec[index];
|
|
|
|
LtsCoordTrans::UtmCoordinate startPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(startPos.lon(), startPos.lat());
|
|
DEBUG_LOG(m_logger) << "startPosUtm: " << startPositionUtm.mgrs();
|
|
double length = 0.0;
|
|
|
|
vecSorted.push_back(startPos);
|
|
|
|
// Mine positions are the new segments
|
|
while (vec.size() > 0)
|
|
{
|
|
Core::GeoPosition2D &pos = vec.first();
|
|
DEBUG_LOG(m_logger) << "pos: " << pos.lon() << ", " << pos.lat();
|
|
|
|
if (pos == startPos)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Remove same Position: " << pos;
|
|
vec.removeFirst();
|
|
continue;
|
|
}
|
|
|
|
length = LtsCoordTrans::squareDistance(startPositionUtm, pos.lon(), pos.lat());
|
|
DEBUG_LOG(m_logger) << "Distance between startPos and pos: " << length;
|
|
|
|
Core::GeoPosition2D newPos = pos;
|
|
for (const Core::GeoPosition2D &pos2 : qAsConst(vec))
|
|
{
|
|
if (pos2 == pos || pos2 == startPos)
|
|
continue;
|
|
|
|
DEBUG_LOG(m_logger) << "pos2: " << pos2.lon() << ", " << pos2.lat();
|
|
|
|
double length2 = LtsCoordTrans::squareDistance(startPositionUtm, pos2.lon(), pos2.lat());
|
|
DEBUG_LOG(m_logger) << "Distance between startPos and pos2: " << length;
|
|
|
|
if (length2 < length)
|
|
{
|
|
newPos = pos2;
|
|
length = length2;
|
|
DEBUG_LOG(m_logger) << "NewPos: length: " << length;
|
|
}
|
|
}
|
|
vecSorted.push_back(newPos);
|
|
vec.removeOne(newPos);
|
|
|
|
startPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(newPos.lon(), newPos.lat(), startPositionUtm.zone);
|
|
DEBUG_LOG(m_logger) << "New startPosUtm: " << startPositionUtm.mgrs();
|
|
}
|
|
|
|
if (vecSorted.size() == 1)
|
|
{
|
|
DEBUG_LOG(m_logger) << "only one segment - create 2 new with distance 10m";
|
|
const LtsCoordTrans::UtmCoordinate pointUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(vecSorted[0].lon(), vecSorted[0].lat());
|
|
LtsCoordTrans::UtmCoordinate point1Utm = pointUtm;
|
|
LtsCoordTrans::UtmCoordinate point2Utm = pointUtm;
|
|
point1Utm.y = pointUtm.y - 5.01;
|
|
point2Utm.y = pointUtm.y + 5.01;
|
|
vecSorted.clear();
|
|
vecSorted.reserve(2);
|
|
|
|
QPointF point = point1Utm.geographic();
|
|
Core::GeoPosition2D pos(point.x(), point.y());
|
|
vecSorted.push_back(pos);
|
|
point = point2Utm.geographic();
|
|
pos.lon(point.x());
|
|
pos.lat(point.y());
|
|
vecSorted.push_back(pos);
|
|
}
|
|
}
|
|
|
|
// for (const auto &secPos : vecSorted)
|
|
// {
|
|
// NOTICE_LOG(m_logger) << secPos;
|
|
// }
|
|
QVector<Database::AesMineAreaSegment> dbSegments;
|
|
double totalLength = 0.0;
|
|
Core::FunctionReplyObject reply = createSegments(vecSorted, m_data.privMineAreaId, dbSegments, totalLength);
|
|
if (reply.error())
|
|
return reply;
|
|
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
|
|
m_mainHandler->db()->removeAesMineAreaSegmentByMineAreaId(m_data.privMineAreaId);
|
|
removeTopics();
|
|
|
|
for (Database::AesMineAreaSegment &segment : dbSegments)
|
|
{
|
|
if (!m_mainHandler->db()->insert(segment))
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error updating AesMineAreaSegment-data in database."));
|
|
}
|
|
|
|
// Mine area is now a line with width 0.0
|
|
m_data.width = 0.0;
|
|
int equipNo = m_data.name.toInt();
|
|
if (equipNo > 0)
|
|
m_data.name = m_data.name + QStringLiteral("_") + QString::number(m_data.id) + QStringLiteral("Minensperre");
|
|
|
|
if (!m_mainHandler->db()->update(m_data))
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error updating AesMineArea-data in database."));
|
|
|
|
trans.commit();
|
|
|
|
// read all Segments from database
|
|
m_segments.clear();
|
|
if (!m_mainHandler->db()->selectByMineAreaId(m_segments, m_data.privMineAreaId)) // db
|
|
{
|
|
ERROR_LOG(m_logger) << "Error reading data from database!";
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error reading triggered mines from database."));
|
|
;
|
|
}
|
|
|
|
std::sort(m_segments.begin(),
|
|
m_segments.end(),
|
|
[](const Database::AesMineAreaSegment &a, const Database::AesMineAreaSegment &b) { return a.elementNumber < b.elementNumber; });
|
|
|
|
m_mineAreaPolygon = Polygons::mineArea(m_segments, m_data.width, m_data.density, m_data.isCenterLine);
|
|
|
|
m_triggeredMinesChanged = true;
|
|
onPublishData();
|
|
|
|
return Core::FunctionReplyObject(m_data.id);
|
|
}
|
|
|
|
template <typename T> Core::FunctionReplyObject ObjectMineArea::createObjectTmpl(const Database::AesMineArea &data, const std::vector<T> &segments)
|
|
{
|
|
DEBUG_LOG(m_logger) << "createObjectTmpl"
|
|
<< " width=" << data.width << " chargeTypeId=" << data.chargeTypeId << " effectDuration=" << data.effectDuration
|
|
<< " segments.size=" << segments.size();
|
|
|
|
auto getRightId = [](int id, bool originOc) -> int { return (originOc) ? (id * -1) : (id); };
|
|
bool originOc = false;
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
m_data = data;
|
|
|
|
// recalc effect state except it's already expired, removed or it's paused.
|
|
if (data.effectStateId != AesEffectState::EsRemoved && data.effectStateId != AesEffectState::EsExpired && data.effectStateId != AesEffectState::EsPaused)
|
|
m_data.effectStateId = AesEffectState::EsUnknown;
|
|
|
|
// On Objects, that are already in DB, segments is empty!
|
|
double totalLength = 0.0;
|
|
Core::FunctionReplyObject reply = createSegments(segments, -1, m_segments, totalLength);
|
|
if (m_data.id <= 0 && reply.error())
|
|
return reply;
|
|
|
|
// New Data!
|
|
if (m_data.id <= 0)
|
|
{
|
|
if (m_segments.isEmpty())
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Could not create mine area without segments."));
|
|
|
|
std::vector<Core::GeoPosition> form;
|
|
ddsVecFromPoly(generateForm(), form);
|
|
|
|
m_data.isCentral = checkIsCentral(form, {}, false);
|
|
|
|
if (m_data.isCentral)
|
|
reply = checkDensity(m_data.density, totalLength, m_mainHandler->mineAreaHandler()->maxCentralSingleMinesCount());
|
|
else
|
|
reply = checkDensity(m_data.density, totalLength, m_mainHandler->mineAreaHandler()->maxSingleMinesCount());
|
|
|
|
if (reply.error())
|
|
return reply;
|
|
|
|
if (m_data.id < 0) // is the id given as negative? make positive again -> used by OC-Laptop
|
|
{
|
|
m_data.id *= -1;
|
|
m_data.priv_entityId = m_data.id; // OC is using id from Tetra and these are unique
|
|
originOc = true;
|
|
}
|
|
|
|
if (!m_mainHandler->db()->insert(m_data))
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Error inserting AesMineArea-data into database."));
|
|
|
|
for (Database::AesMineAreaSegment &segment : m_segments)
|
|
{
|
|
// update ids
|
|
segment.id = 0;
|
|
segment.mineAreaId = m_data.privMineAreaId; // db
|
|
DEBUG_LOG(m_logger) << "m_mainHandler->db()->insert(segment) segment.id=" << segment.id << " mineAreaId=" << segment.mineAreaId;
|
|
if (!m_mainHandler->db()->insert(segment))
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Error inserting AesMineAreaSegment-data into database."));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG(m_logger) << "else";
|
|
// No update. Will only be called after gathering that data from db
|
|
|
|
// read all Segments from database
|
|
m_segments.clear();
|
|
if (!m_mainHandler->db()->selectByMineAreaId(m_segments, m_data.privMineAreaId)) // db
|
|
{
|
|
ERROR_LOG(m_logger) << "Error reading data from database!";
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error reading triggered mines from database."));
|
|
;
|
|
}
|
|
|
|
std::sort(m_segments.begin(),
|
|
m_segments.end(),
|
|
[](const Database::AesMineAreaSegment &a, const Database::AesMineAreaSegment &b) { return a.elementNumber < b.elementNumber; });
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "Vor m_segments: " << m_segments.size() << " width: " << m_data.width << " dataId: " << m_data.id << " privateAreaId: " << m_data.privMineAreaId;
|
|
|
|
m_mineAreaPolygon = Polygons::mineArea(m_segments, m_data.width, m_data.density, m_data.isCenterLine);
|
|
|
|
if (isVirtualMine())
|
|
{
|
|
DEBUG_LOG(m_logger) << "Vor generateVirtualMines";
|
|
generateVirtualMines();
|
|
clearBuildings();
|
|
}
|
|
|
|
// read all Triggered Mines from database
|
|
// Store all those of this mine area in m_triggeredMines
|
|
auto cb = [this](const Database::AesMineAreaTriggeredMines &mine) { m_triggeredMines.insert(mine.mineId, mine); };
|
|
if (!m_mainHandler->db()->selectByMineAreaId(cb, m_data.id))
|
|
{
|
|
ERROR_LOG(m_logger) << "Error reading data from database!";
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error reading triggered mines from database."));
|
|
;
|
|
}
|
|
|
|
// Set TriggeredExplosions Base Data
|
|
m_tis.id(1000 * m_data.id + AesType::AtMineAreaSingleMines);
|
|
m_tis.aesItemId(m_data.id);
|
|
m_tis.aesTypeId(AesType::AtMineAreaSingleMines);
|
|
|
|
// Set DUS-Mine status Base Data
|
|
m_dms.id(m_data.id);
|
|
|
|
trans.commit();
|
|
|
|
// to get initial dds objects for empty m_triggeredMines
|
|
m_triggeredMinesChanged = true;
|
|
|
|
// start state machine (will call onPublish() to publish MineArea, AIV and m_tes)
|
|
m_publishDataTimer->start();
|
|
|
|
checkEnterNewState();
|
|
DEBUG_LOG(m_logger) << "Vor return Core::FunctionReplyObject(getRightId(" << m_data.id << ", " << originOc << ")";
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc));
|
|
}
|
|
|
|
template <typename T> Core::FunctionReplyObject ObjectMineArea::updateObjectTmpl(const Database::AesMineArea &data, const std::vector<T> &segments)
|
|
{
|
|
DEBUG_LOG(m_logger) << "updateObjectTmpl "
|
|
<< " data.id=" << data.id << " externalId=" << data.externalId << " chargeTypeId=" << data.chargeTypeId << " segments.size=" << segments.size();
|
|
Core::FunctionReplyObject ret = ObjectBaseTmpl::updateObject(data);
|
|
if (ret.error() || (isOnlyObjectNameModifiable() && (data.id > 0))) // data.id > 0 -> negative values from oc always get a full update
|
|
return ret;
|
|
|
|
// only allow some fields
|
|
// Todo: Complete???
|
|
Database::AesMineArea tmpData(m_data);
|
|
tmpData.id = std::abs(data.id);
|
|
tmpData.name = data.name;
|
|
tmpData.effectStart = data.effectStart;
|
|
tmpData.effectDuration = data.effectDuration;
|
|
tmpData.participantId = data.participantId;
|
|
tmpData.partyId = data.partyId;
|
|
tmpData.effectMarkingInTa = data.effectMarkingInTa;
|
|
tmpData.density = data.density;
|
|
tmpData.layingProcedureTypeId = data.layingProcedureTypeId;
|
|
tmpData.isLaidHidden = data.isLaidHidden;
|
|
tmpData.width = data.width;
|
|
tmpData.isCenterLine = data.isCenterLine;
|
|
tmpData.effectStateId = data.effectStateId; // overwrite for oc
|
|
tmpData.properties = data.properties;
|
|
|
|
// before updating chargeTypeId: if chargeTypeId changed, check if already was DUS-area and release mines
|
|
bool wasDusArea = isDusMineArea();
|
|
bool wasConcreteArea = isConcreteMine();
|
|
if ((tmpData.chargeTypeId != data.chargeTypeId))
|
|
{
|
|
if (wasDusArea || wasConcreteArea)
|
|
{
|
|
QList<Database::AesMineAreaTriggeredMines> minesToBeReleased = m_triggeredMines.values();
|
|
m_triggeredMines.clear();
|
|
m_triggeredMinesChanged = true;
|
|
emit releaseMines(m_data.id, minesToBeReleased);
|
|
|
|
DEBUG_LOG(m_logger) << "Cleared m_triggeredMines for minearea-ID " << m_data.id;
|
|
}
|
|
}
|
|
tmpData.chargeTypeId = data.chargeTypeId;
|
|
|
|
QVector<Database::AesMineAreaSegment> dbSegments;
|
|
double totalLength = 0.0;
|
|
Core::FunctionReplyObject reply = createSegments(segments, m_data.privMineAreaId, dbSegments, totalLength);
|
|
if (reply.error())
|
|
return reply;
|
|
|
|
if (m_data.isCentral)
|
|
reply = checkDensity(tmpData.density, totalLength, 1500);
|
|
else
|
|
reply = checkDensity(tmpData.density, totalLength, 1200);
|
|
|
|
if (reply.error())
|
|
return reply;
|
|
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
DEBUG_LOG(m_logger) << "removeAesMineAreaSegmentByMineAreaId m_data.privMineAreaId=" << m_data.privMineAreaId;
|
|
m_mainHandler->db()->removeAesMineAreaSegmentByMineAreaId(m_data.privMineAreaId);
|
|
for (Database::AesMineAreaSegment &segment : dbSegments)
|
|
{
|
|
if (!m_mainHandler->db()->insert(segment))
|
|
return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error updating AesMineAreaSegment-data in database."));
|
|
}
|
|
|
|
// if Segments changed for DUS-area, release mines that are not in the new polygon
|
|
// canHandleMine() needs the new m_mineAreaPolygon
|
|
m_mineAreaPolygon = Polygons::mineArea(dbSegments, tmpData.width, tmpData.density, tmpData.isCenterLine);
|
|
bool segmentsChanged = false;
|
|
if (m_segments != dbSegments && (isDusMineArea() || isConcreteMine()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Shape changed: release mines that are not inside the minearea anymore.";
|
|
|
|
QMutableMapIterator<int, Database::AesMineAreaTriggeredMines> it(m_triggeredMines);
|
|
QList<Database::AesMineAreaTriggeredMines> releasedMines;
|
|
while (it.hasNext())
|
|
{
|
|
auto mine = it.next().value();
|
|
if (!canHandleMine(mine.mineId, mine.chargeTypeId, mine.position))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Releasing mine " << mine.mineId << " from mine Area " << m_data.id << " as it is not inside the minearea anymore.";
|
|
releasedMines << mine;
|
|
it.remove();
|
|
}
|
|
}
|
|
|
|
if (!releasedMines.isEmpty())
|
|
{
|
|
m_triggeredMinesChanged = true;
|
|
emit releaseMines(m_data.id, releasedMines);
|
|
}
|
|
|
|
segmentsChanged = true;
|
|
}
|
|
|
|
m_segments = dbSegments;
|
|
|
|
if (!m_mainHandler->db()->update(tmpData))
|
|
return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error updating mine area with id %1 in database.")).arg(m_data.id);
|
|
m_data = std::move(tmpData);
|
|
|
|
trans.commit();
|
|
|
|
// needs to be emitted after "m_data = std::move(tmpData);" or we will get assigned our released DUS-mines
|
|
if (segmentsChanged)
|
|
emit shapeChanged(m_data.id);
|
|
|
|
// needs to be emitted after "m_data = std::move(tmpData);" so isDusArea() operates on new data
|
|
if ((isDusMineArea() && !wasDusArea) || (isConcreteMine() && !wasConcreteArea))
|
|
{
|
|
m_triggeredMines.clear(); // was virtual, is now DUS -> delete triggered
|
|
m_triggeredMinesChanged = true;
|
|
}
|
|
else if (isVirtualMine())
|
|
{
|
|
generateVirtualMines();
|
|
clearBuildings();
|
|
if (wasDusArea || wasConcreteArea)
|
|
removeDusMineStateTopic();
|
|
}
|
|
|
|
m_publishDataTimer->start();
|
|
checkEnterNewState();
|
|
|
|
return Core::FunctionReplyObject(data.id);
|
|
}
|
|
|
|
/*!
|
|
* \brief ObjectMineArea::createObjectFromMineData
|
|
* \param data
|
|
* \param segment
|
|
* \return
|
|
*/
|
|
Core::FunctionReplyObject ObjectMineArea::createObjectFromMineData(const Database::AesMineArea &data,
|
|
const std::vector<Core::GeoPosition2D> &segment,
|
|
const std::vector<MineData> &mineData)
|
|
{
|
|
DEBUG_LOG(m_logger) << "--[createObjectFromMineData] "
|
|
<< " width=" << data.width << " chargeTypeId=" << data.chargeTypeId << " effectDuration=" << data.effectDuration
|
|
<< " segments.size=" << segment.size();
|
|
|
|
auto getRightId = [](int id, bool originOc) -> int { return (originOc) ? (id * -1) : (id); };
|
|
bool originOc = false;
|
|
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
m_data = data;
|
|
m_data.properties = data.properties;
|
|
|
|
// recalc effect state except it's already expired, removed or it's paused.
|
|
if (data.effectStateId != AesEffectState::EsRemoved && data.effectStateId != AesEffectState::EsExpired && data.effectStateId != AesEffectState::EsPaused)
|
|
m_data.effectStateId = AesEffectState::EsUnknown;
|
|
|
|
// On Objects, that are already in DB, segments is empty!
|
|
double totalLength = 0.0;
|
|
Core::FunctionReplyObject reply = createSegments(segment, -1, m_segments, totalLength);
|
|
if (reply.error())
|
|
return reply;
|
|
|
|
if (m_segments.isEmpty())
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Could not create mine area without segments."));
|
|
|
|
m_data.isCentral = true; // checkIsCentral(form, {}, false);
|
|
|
|
if (m_data.id < 0) // is the id given as negative? make positive again -> used by OC-Laptop
|
|
{
|
|
m_data.id *= -1;
|
|
m_data.priv_entityId = m_data.id; // OC is using id from Tetra and these are unique
|
|
originOc = true;
|
|
}
|
|
|
|
if (!m_mainHandler->db()->insert(m_data))
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Error inserting AesMineArea-data into database."));
|
|
|
|
// db insert
|
|
for (Database::AesMineAreaSegment &segment : m_segments)
|
|
{
|
|
segment.id = 0;
|
|
segment.mineAreaId = m_data.privMineAreaId; // db
|
|
DEBUG_LOG(m_logger) << "--[createObjectFromMineData] insert into DB "
|
|
<< " privMineAreaId=" << m_data.privMineAreaId << " pos start: " << segment.startPosition << " pos End: " << segment.endPosition;
|
|
if (!m_mainHandler->db()->insert(segment))
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc), tr_lts("dds", "Error inserting AesMineAreaSegment-data into database."));
|
|
}
|
|
|
|
clearBuildings();
|
|
|
|
// read all Triggered Mines from database
|
|
if (!addMineData(mineData))
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error adding triggered mines to database."));
|
|
|
|
// Set TriggeredExplosions Base Data
|
|
m_tis.id(1000 * m_data.id + AesType::AtMineAreaSingleMines);
|
|
m_tis.aesItemId(m_data.id);
|
|
m_tis.aesTypeId(AesType::AtMineAreaSingleMines);
|
|
|
|
// Set DUS-Mine status Base Data
|
|
m_dms.id(m_data.id);
|
|
|
|
trans.commit();
|
|
|
|
// to get initial dds objects for empty m_triggeredMines
|
|
m_triggeredMinesChanged = true;
|
|
|
|
// start state machine (will call onPublish() to publish MineArea, AIV and m_tes)
|
|
m_publishDataTimer->start();
|
|
|
|
checkEnterNewState();
|
|
DEBUG_LOG(m_logger) << "--[createObjectFromMineData] end new ------> m_triggeredMines count: " << m_triggeredMines.count();
|
|
return Core::FunctionReplyObject(getRightId(m_data.id, originOc));
|
|
}
|
|
|
|
/*!
|
|
* \brief ObjectMineArea::updateObjectFromReferenceAesItem
|
|
* \param data
|
|
* \param segments
|
|
* \return
|
|
*/
|
|
Core::FunctionReplyObject ObjectMineArea::updateObjectFromMineData(const Database::AesMineArea &data,
|
|
const std::vector<Core::GeoPosition2D> &segments,
|
|
const std::vector<MineData> &mineData)
|
|
{
|
|
DEBUG_LOG(m_logger) << "[updateObjectFromMineData] >> \ndata.id= " << data.id << " \nprivMineAreaId: " << m_data.privMineAreaId
|
|
<< " \nchargeTypeId= " << data.chargeTypeId << " \nmineData.size= " << segments.size() << " partyId: " << data.partyId;
|
|
Database::AesMineArea tmpData(data);
|
|
|
|
Core::FunctionReplyObject ret;
|
|
|
|
tmpData.id = std::abs(data.id);
|
|
tmpData.name = data.name;
|
|
tmpData.effectStart = data.effectStart;
|
|
tmpData.effectDuration = data.effectDuration;
|
|
tmpData.participantId = data.participantId;
|
|
tmpData.partyId = data.partyId;
|
|
tmpData.effectMarkingInTa = data.effectMarkingInTa;
|
|
tmpData.density = data.density;
|
|
tmpData.layingProcedureTypeId = data.layingProcedureTypeId;
|
|
tmpData.isLaidHidden = data.isLaidHidden;
|
|
tmpData.width = data.width;
|
|
tmpData.isCenterLine = data.isCenterLine;
|
|
tmpData.effectStateId = data.effectStateId; // overwrite for oc
|
|
tmpData.properties = data.properties;
|
|
tmpData.chargeTypeId = data.chargeTypeId;
|
|
|
|
QVector<Database::AesMineAreaSegment> dbSegments;
|
|
double totalLength = 0.0;
|
|
ret = createSegments(segments, m_data.privMineAreaId, m_segments, totalLength);
|
|
if (ret.error())
|
|
return ret;
|
|
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
DEBUG_LOG(m_logger) << "--[updateObjectFromMineData] remove existing segments...";
|
|
m_mainHandler->db()->removeAesMineAreaSegmentByMineAreaId(m_data.privMineAreaId);
|
|
|
|
for (Database::AesMineAreaSegment &segment : m_segments)
|
|
{
|
|
if (!m_mainHandler->db()->insert(segment))
|
|
return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error updating AesMineAreaSegment-data in database."));
|
|
}
|
|
|
|
if (!m_mainHandler->db()->update(tmpData))
|
|
return Core::FunctionReplyObject(data.id, tr_lts("dds", "Error updating mine area with id %1 in database.")).arg(m_data.id);
|
|
|
|
m_mineAreaPolygon = Polygons::mineArea(m_segments, tmpData.width, tmpData.density, tmpData.isCenterLine);
|
|
m_data = tmpData;
|
|
|
|
trans.commit();
|
|
// add mine data to trigger mine
|
|
if (!addMineData(mineData))
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error adding triggered mines to database."));
|
|
|
|
// if (selectMineFromDb(m_triggeredMines))
|
|
// return;
|
|
|
|
clearBuildings();
|
|
|
|
m_triggeredMinesChanged = true;
|
|
m_publishDataTimer->start();
|
|
checkEnterNewState();
|
|
|
|
DEBUG_LOG(m_logger) << "--[updateObjectFromMineData] end add ------> m_triggeredMines count: " << m_triggeredMines.count();
|
|
return Core::FunctionReplyObject(data.id);
|
|
}
|
|
|
|
double ObjectMineArea::squareLen(const QPointF &p1, const QPointF &p2) const
|
|
{
|
|
double vx = p2.x() - p1.x();
|
|
double vy = p2.y() - p1.y();
|
|
return vx * vx + vy * vy;
|
|
}
|
|
|
|
double ObjectMineArea::distancePointLine(const QPointF &pUtm, const QPointF &l1Utm, const QPointF &l2Utm) const
|
|
{
|
|
DEBUG_LOG(m_logger) << "p=" << pUtm.x() << "/" << pUtm.y() << " l1=" << l1Utm.x() << "/" << l1Utm.y() << " l2=" << l2Utm.x() << "/" << l2Utm.y();
|
|
const double u = (((pUtm.x() - l1Utm.x()) * (l2Utm.x() - l1Utm.x())) + ((pUtm.y() - l1Utm.y()) * (l2Utm.y() - l1Utm.y()))) / squareLen(l2Utm, l1Utm);
|
|
|
|
if (u < 0.0f || u > 1.0f)
|
|
{
|
|
QLineF pToP1(pUtm, l1Utm);
|
|
QLineF pToP2(pUtm, l2Utm);
|
|
qreal length1 = pToP1.length();
|
|
qreal length2 = pToP2.length();
|
|
if (length1 < length2)
|
|
{
|
|
DEBUG_LOG(m_logger) << "a distancePointLine p=" << pUtm.x() << "/" << pUtm.y() << " line: p1=" << l1Utm.x() << "/" << l1Utm.y() << " p2=" << l2Utm.x() << "/"
|
|
<< l2Utm.y() << " distance=" << length1 << ", return=" << length1;
|
|
return length1;
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG(m_logger) << "b distancePointLine p=" << pUtm.x() << "/" << pUtm.y() << " line: p1=" << l1Utm.x() << "/" << l1Utm.y() << " p2=" << l2Utm.x() << "/"
|
|
<< l2Utm.y() << " distance=" << length2 << ", return=" << length2;
|
|
return length2;
|
|
}
|
|
}
|
|
|
|
QPointF intersectPnt(l1Utm.x() + u * (l2Utm.x() - l1Utm.x()), l1Utm.y() + u * (l2Utm.y() - l1Utm.y()));
|
|
|
|
double distance = sqrt(squareLen(pUtm, intersectPnt));
|
|
|
|
DEBUG_LOG(m_logger) << "c distancePointLine p=" << pUtm.x() << "/" << pUtm.y() << " line: p1=" << l1Utm.x() << "/" << l1Utm.y() << " p2=" << l2Utm.x() << "/"
|
|
<< l2Utm.y() << " distance=" << distance;
|
|
return distance;
|
|
}
|
|
|
|
bool ObjectMineArea::containsPointInExpandedMinefield(const QPointF &positionGeo) const
|
|
{
|
|
QPolygonF mineAreaPolygonUtm;
|
|
mineAreaPolygonUtm.reserve(m_mineAreaPolygon.size());
|
|
|
|
const LtsCoordTrans::UtmCoordinate pointUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(positionGeo.x(), positionGeo.y());
|
|
DEBUG_LOG(m_logger) << "centerPoint=" << pointUtm.x << "/" << pointUtm.y << " zone=" << pointUtm.zone;
|
|
|
|
for (const auto &pos : m_mineAreaPolygon) // Last Pos just for Length and Direction...
|
|
{
|
|
const LtsCoordTrans::UtmCoordinate &pointMineAreaUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(pos, pointUtm.zone);
|
|
DEBUG_LOG(m_logger) << "pointMineAreaUtm=" << pointMineAreaUtm.x << "/" << pointMineAreaUtm.y << " zone=" << pointMineAreaUtm.zone;
|
|
|
|
mineAreaPolygonUtm.push_back(QPointF(pointMineAreaUtm.x, pointMineAreaUtm.y));
|
|
}
|
|
|
|
const double distance = 0.5;
|
|
|
|
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) << "ObjectMineArea::containsPoint return true";
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ObjectMineArea::canHandleMine(int mineId, int chargeTypeId, const QPointF &position) const
|
|
{
|
|
DEBUG_LOG(m_logger) << "Requesting Mine Area " << m_data.id << " to handle the DUS-Mine " << mineId << " . Request has " << (position.isNull() ? "no " : "valid ")
|
|
<< "position.";
|
|
|
|
// DUS mine triggers, that are not yet associated to this mine area and are not within the polygon shall be ignored.
|
|
if (!isDusMineArea() && !isConcreteMine())
|
|
DEBUG_LOG(m_logger) << "Mine Area " << m_data.id << " is not a DUS mine area.";
|
|
|
|
// EffectState has to be Created, Prepared, Active and Paused Areas
|
|
if (m_data.effectStateId.underlying() < AesEffectState::EsCreated)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Mine Area " << m_data.id << " can not handle mine because its current AesEffectState is"
|
|
<< AreaEffectSimulation::toString(m_data.effectStateId);
|
|
return false;
|
|
}
|
|
|
|
if (!position.isNull())
|
|
{
|
|
// DEBUG_LOG(m_logger) << "Mine Area " << m_data.id << (m_mineAreaPolygon.containsPoint(position, Qt::OddEvenFill) ? " contains " : " does not contain ") << "mine
|
|
// position.";
|
|
DEBUG_LOG(m_logger) << "Mine Area " << m_data.id << (containsPointInExpandedMinefield(position) ? " contains " : " does not contain ") << "mine position.";
|
|
if (containsPointInExpandedMinefield(position))
|
|
return true;
|
|
}
|
|
|
|
if (chargeTypeId > 0 && m_data.chargeTypeId != chargeTypeId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Mine Area" << m_data.id << " does not have the matching chargeTypeId " << chargeTypeId;
|
|
return false;
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "Mine is " << (m_triggeredMines.contains(mineId) ? "part of " : "not part of ") << "this mine area."
|
|
<< " mineId=" << mineId << "m_triggeredMines.size=" << m_triggeredMines.size();
|
|
|
|
if (!m_triggeredMines.contains(mineId) && (position.isNull() || !m_mineAreaPolygon.containsPoint(position, Qt::OddEvenFill)))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Therefore, we skip this mine area.";
|
|
return false;
|
|
}
|
|
DEBUG_LOG(m_logger) << "Mine Area " << m_data.id << " can handle this mine";
|
|
return true;
|
|
}
|
|
|
|
bool ObjectMineArea::triggerMine(int mineId, bool trigger, const QPointF &position)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Triggering mine " << mineId << " trigger " << trigger;
|
|
return triggerMines({mineId}, trigger, position);
|
|
}
|
|
|
|
bool ObjectMineArea::updateMine(Database::AesMineAreaTriggeredMines &mine)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Updating mine " << mine.mineId << " with dbId " << mine.id << ", isTriggerd: " << mine.isTriggered;
|
|
mine.mineAreaId = m_data.id;
|
|
// mine.isTriggered = true;
|
|
mine.triggerTime = QDateTime::fromMSecsSinceEpoch(partitionTimeMSec());
|
|
|
|
m_triggeredMines[mine.mineId] = mine;
|
|
|
|
if (!m_mainHandler->db()->update(mine))
|
|
ERROR_LOG(m_logger) << "Error updating mine " << mine.mineId << " in db!";
|
|
|
|
onPublishData();
|
|
return true;
|
|
}
|
|
|
|
void ObjectMineArea::updateMineByOldMineId(Database::AesMineAreaTriggeredMines &mine, qint32 oldMineId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Updating mine by oldMineId " << oldMineId << " with new mineId " << mine.mineId << " and dbId " << mine.id
|
|
<< ", isTriggerd: " << mine.isTriggered;
|
|
|
|
mine.mineAreaId = m_data.id;
|
|
mine.triggerTime = QDateTime::fromMSecsSinceEpoch(partitionTimeMSec());
|
|
|
|
m_triggeredMines.remove(oldMineId);
|
|
m_triggeredMines.insert(mine.mineId, mine);
|
|
|
|
if (!m_mainHandler->db()->update(mine))
|
|
ERROR_LOG(m_logger) << "Error updating mine " << mine.mineId << " in db!";
|
|
|
|
onPublishData();
|
|
}
|
|
|
|
int ObjectMineArea::mineCount()
|
|
{
|
|
double totalLength = 0.0;
|
|
for (const Database::AesMineAreaSegment &segment : qAsConst(m_segments))
|
|
{
|
|
LtsCoordTrans::UtmCoordinate startPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(segment.startPosition);
|
|
LtsCoordTrans::UtmCoordinate endPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(segment.endPosition, startPositionUtm.zone);
|
|
const QLineF line({startPositionUtm.x, startPositionUtm.y}, {endPositionUtm.x, endPositionUtm.y});
|
|
totalLength += line.length();
|
|
}
|
|
|
|
return qRound(totalLength * m_data.density);
|
|
}
|
|
|
|
bool ObjectMineArea::triggerMines(const QVector<int> &mineIds, bool trigger, const QPointF &position)
|
|
{
|
|
DEBUG_LOG(m_logger) << "triggerMines mineIds.size()=" << mineIds.size() << " trigger=" << trigger;
|
|
// Do not trigger different mines with the same positition...
|
|
if (mineIds.size() > 1 && !position.isNull())
|
|
return false;
|
|
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
for (int mineId : mineIds)
|
|
{
|
|
Database::AesMineAreaTriggeredMines &tm = m_triggeredMines[mineId];
|
|
tm.mineAreaId = m_data.id;
|
|
tm.mineId = mineId;
|
|
tm.isTriggered = trigger;
|
|
tm.chargeTypeId = m_data.chargeTypeId;
|
|
if (trigger)
|
|
tm.triggerTime = QDateTime::fromMSecsSinceEpoch(partitionTimeMSec());
|
|
if (!position.isNull())
|
|
tm.position = position;
|
|
|
|
DEBUG_LOG(m_logger) << "tm.id: " << tm.id << ", mineId: " << tm.mineId << ", mineAreaId: " << tm.mineAreaId;
|
|
bool bOk = true;
|
|
if (!tm.id)
|
|
{
|
|
ERROR_LOG(m_logger) << "insert triggered Mine";
|
|
bOk = m_mainHandler->db()->insert(tm);
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << "update triggered Mine";
|
|
bOk = m_mainHandler->db()->update(tm);
|
|
}
|
|
|
|
if (!bOk)
|
|
{
|
|
ERROR_LOG(m_logger) << "Error " << (tm.id ? "updating " : "inserting ") << "mine " << tm.mineId;
|
|
return false;
|
|
}
|
|
}
|
|
trans.commit();
|
|
|
|
onPublishData();
|
|
return true;
|
|
}
|
|
|
|
void ObjectMineArea::generateVirtualMines()
|
|
{
|
|
DEBUG_LOG(m_logger) << "generateVirtualMines";
|
|
|
|
// Ensure, all virtual mines are in map.
|
|
QVector<QPointF> mineposVec = Polygons::minePositions(m_data, m_segments);
|
|
|
|
Database::AesMineAreaTriggeredMines tm;
|
|
tm.mineAreaId = m_data.id;
|
|
const auto triggerdMinesCopy = m_triggeredMines;
|
|
m_triggeredMines.clear();
|
|
// use mineposVec.size(), since mineCount counts all mines (incl. removed mines in buildings)
|
|
for (int i = 0; i < mineposVec.size(); ++i)
|
|
{
|
|
tm.mineId = i;
|
|
tm.isTriggered = triggerdMinesCopy.value(i).isTriggered;
|
|
m_triggeredMines[i] = tm;
|
|
m_triggeredMines[i].position = mineposVec[i]; // Set Position for all mines. i starts at 1 but mineposVec at 0
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "Virtual mines for minearea-ID " << m_data.id << " generated.";
|
|
}
|
|
|
|
bool ObjectMineArea::addMineData(const std::vector<MineData> &mineData)
|
|
{
|
|
// get existing mine position from db before adding new one
|
|
if (!selectMineFromDb(m_triggeredMines))
|
|
ERROR_LOG(m_logger) << "--[addMineData] ERROR selectMineFromDb RETURN FALSE ";
|
|
|
|
QVector<int> mineIds;
|
|
int idx = m_triggeredMines.isEmpty() ? 0 : m_triggeredMines.lastKey() + 1;
|
|
QMap<int, Database::AesMineAreaTriggeredMines> newMines;
|
|
// sort mineIds and get last entry which is the greatest id to start counting from it
|
|
|
|
Database::AesMineAreaTriggeredMines tm;
|
|
DBTransaction trans(m_mainHandler->db()->database());
|
|
// insert mine in der DB later fill m_triggerMine with db->selectByMineAreaId(..)
|
|
for (const auto &mine : mineData)
|
|
{
|
|
tm.id = 0;
|
|
tm.mineAreaId = m_data.id;
|
|
tm.chargeTypeId = m_data.chargeTypeId;
|
|
tm.mineId = idx++;
|
|
tm.appearenceTime = QDateTime::fromMSecsSinceEpoch(mine.appearenceTime());
|
|
tm.armingTime = QDateTime::fromMSecsSinceEpoch(mine.armingTime());
|
|
tm.position = QPointF(mine.position().lon(), mine.position().lat());
|
|
if (!m_mainHandler->db()->insert(tm))
|
|
{
|
|
ERROR_LOG(m_logger) << "--[addMineData] Couldn't insert trigger mine into db";
|
|
return false;
|
|
}
|
|
newMines.insert(tm.mineId, tm);
|
|
}
|
|
trans.commit();
|
|
|
|
m_triggeredMines.insert(newMines);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ObjectMineArea::selectMineFromDb(QMap<int, Database::AesMineAreaTriggeredMines> &outTriggerMines)
|
|
{
|
|
QMap<int, Database::AesMineAreaTriggeredMines> tmpMines;
|
|
// Store all those of this mine area in m_triggeredMines
|
|
auto cb = [this, &tmpMines](const Database::AesMineAreaTriggeredMines &mine)
|
|
{
|
|
DEBUG_LOG(m_logger) << "--[selectMineFromDb] >>> m_triggeredMines.insert mineId=" << mine.mineId << " mineAreaId=" << mine.mineAreaId;
|
|
tmpMines.insert(mine.mineId, mine);
|
|
};
|
|
|
|
if (!m_mainHandler->db()->selectByMineAreaId(cb, m_data.id))
|
|
{
|
|
ERROR_LOG(m_logger) << "Error reading data from database!";
|
|
return false;
|
|
}
|
|
outTriggerMines = tmpMines;
|
|
return true;
|
|
}
|
|
|
|
void ObjectMineArea::removeDusMineStateTopic()
|
|
{
|
|
DEBUG_LOG(m_logger) << "removeDusMineStateTopic";
|
|
auto dts = m_mainHandler->dds()->dts();
|
|
dts->removeDusMineStateByKey(m_data.id);
|
|
}
|
|
|
|
bool ObjectMineArea::resetMine(int mineId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "resetMine mineId=" << mineId;
|
|
// single reset only allowed in dus mine areas
|
|
if (!isDusMineArea() && !isConcreteMine())
|
|
return false;
|
|
DEBUG_LOG(m_logger) << "Trying to reset mine with id " << mineId;
|
|
if (!m_triggeredMines.contains(mineId))
|
|
return false;
|
|
DEBUG_LOG(m_logger) << "m_data.id: " << m_data.id;
|
|
return triggerMine(mineId, false);
|
|
}
|
|
|
|
bool ObjectMineArea::resetMines()
|
|
{
|
|
DEBUG_LOG(m_logger) << "Resetting all mines in area " << m_data.id;
|
|
if (m_triggeredMines.isEmpty())
|
|
return true;
|
|
|
|
bool bOk = true;
|
|
auto it = m_triggeredMines.begin();
|
|
while (it != m_triggeredMines.end())
|
|
{
|
|
Database::AesMineAreaTriggeredMines &tm = (*it);
|
|
if (tm.isTriggered)
|
|
{
|
|
tm.isTriggered = false;
|
|
bOk &= m_mainHandler->db()->update(tm);
|
|
}
|
|
++it;
|
|
}
|
|
|
|
if (!bOk)
|
|
return false;
|
|
|
|
onPublishData();
|
|
|
|
return true;
|
|
}
|
|
|
|
Database::AesMineAreaTriggeredMines ObjectMineArea::takeMine(int mineId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "takeMine mineId=" << mineId;
|
|
if (!m_triggeredMines.contains(mineId))
|
|
return Database::AesMineAreaTriggeredMines();
|
|
|
|
Database::AesMineAreaTriggeredMines ret = m_triggeredMines.value(mineId);
|
|
m_triggeredMines.remove(mineId);
|
|
|
|
onPublishData();
|
|
return ret;
|
|
}
|
|
|
|
double ObjectMineArea::singleMineRadius() const
|
|
{
|
|
double singleMineRadiusResult = m_mainHandler->dds()->pdmDtc()->impactRadius(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId);
|
|
DEBUG_LOG(m_logger) << "singleMineRadius=" << singleMineRadiusResult;
|
|
return singleMineRadiusResult;
|
|
}
|
|
|
|
void ObjectMineArea::onPublishData()
|
|
{
|
|
DEBUG_LOG(m_logger) << "onPublishData()";
|
|
DDSConnector::CoherentSet cs(m_mainHandler->dds()->ddsConnector());
|
|
ObjectBaseTmpl::onPublishData();
|
|
|
|
if (!m_mainHandler->db()->update(m_data))
|
|
ERROR_LOG(m_logger) << "Error updating AesMineSingle-data in database";
|
|
|
|
std::vector<MineSegment> segVec;
|
|
segVec.reserve(m_segments.size());
|
|
for (const auto &segment : qAsConst(m_segments))
|
|
segVec.emplace_back(
|
|
MineSegment(Core::GeoPosition2D(segment.startPosition.x(), segment.startPosition.y()), Core::GeoPosition2D(segment.endPosition.x(), segment.endPosition.y())));
|
|
|
|
// Todo: Enhancement:
|
|
// store this as member and compare (without partitionTime!) before sending!
|
|
|
|
// calc width/height from m_mineAreaPolygon (geopos!)
|
|
const QRectF boundingRect = m_mineAreaPolygon.boundingRect();
|
|
const LtsCoordTrans::UtmCoordinate tl(LtsCoordTrans::UtmCoordinate::fromGeographic(boundingRect.topLeft()));
|
|
const LtsCoordTrans::UtmCoordinate br(LtsCoordTrans::UtmCoordinate::fromGeographic(boundingRect.bottomRight(), tl.zone));
|
|
|
|
MineArea ddsData(m_data.id,
|
|
m_data.name.toStdString(),
|
|
effectState() == AesEffectState::EsRemoved,
|
|
false,
|
|
partitionTimeMSec(),
|
|
{m_data.chargeTypeId, 0, 0},
|
|
m_data.effectDuration,
|
|
toOptional(toMSec(m_data.effectStart)),
|
|
m_data.effectStateId,
|
|
m_data.effectSourceId,
|
|
toOptional(m_data.partyId),
|
|
toOptional(m_data.participantId),
|
|
m_data.effectMarkingInTa,
|
|
m_data.externalId,
|
|
m_data.isCentral,
|
|
m_data.properties.toStdString(),
|
|
m_data.density,
|
|
0.5,
|
|
m_data.width,
|
|
Core::GeoPosition2D(boundingRect.topLeft().x(), // Is bottomLeft as coordinate sytems
|
|
boundingRect.topLeft().y()), // as QRect has its origin at top left
|
|
qAbs(tl.x - br.x),
|
|
qAbs(tl.y - br.y),
|
|
segVec,
|
|
singleMineRadius(),
|
|
m_data.isLaidHidden,
|
|
m_data.layingProcedureTypeId,
|
|
isDusMineArea(),
|
|
isReactivatableDusMineArea(),
|
|
m_data.isCenterLine,
|
|
isConcreteMine(),
|
|
true);
|
|
|
|
m_mainHandler->dds()->dts()->publish(ddsData);
|
|
publishTriggeredImpacts();
|
|
if (isDusMineArea() || isConcreteMine())
|
|
publishDusMineStatus();
|
|
publishAiv();
|
|
|
|
m_triggeredMinesChanged = false;
|
|
}
|
|
|
|
void ObjectMineArea::publishAiv()
|
|
{
|
|
DEBUG_LOG(m_logger) << "publishAiv()";
|
|
// Todo: calculate unique id.
|
|
// Better than aiv.id(1000 * aesItemId + AesType::AtMineArea);
|
|
|
|
AesItemView aiv(m_aiv);
|
|
aiv.aesItemId(m_data.id);
|
|
aiv.chargeStruct().chargeTypeId(m_data.chargeTypeId);
|
|
aiv.effectStart(toMSec(m_data.effectStart));
|
|
ddsVecFromPoly(m_mineAreaPolygon, aiv.form());
|
|
const auto br = boundingRect(); // m_mineAreaPolygon.boundingRect()
|
|
aiv.boundingBoxBottomLeft(Core::GeoPosition2D(br.bottomLeft().x(), br.bottomLeft().y()));
|
|
aiv.boundingBoxTopRight(Core::GeoPosition2D(br.topRight().x(), br.topRight().y()));
|
|
aiv.aesTypeId(AesType::AtMineArea);
|
|
aiv.effectStateId(effectState());
|
|
aiv.id(1000 * m_data.id + AesType::AtMineArea);
|
|
if (m_data.partyId != 0)
|
|
aiv.partyId(m_data.partyId);
|
|
|
|
checkIsCentralAndPublish(aiv.form(), m_aiv.form(), m_data.isCentral);
|
|
|
|
if (aiv != m_aiv)
|
|
{
|
|
DEBUG_LOG(m_logger) << "aiv changed - publish";
|
|
aiv.sampleTime(partitionTimeMSec());
|
|
m_aiv = aiv;
|
|
m_mainHandler->dds()->dts()->publish(aiv);
|
|
emit newAiv(aiv);
|
|
}
|
|
|
|
AesItemView aiv2(m_aiv2);
|
|
aiv2.aesItemId(m_data.id);
|
|
aiv2.chargeStruct().chargeTypeId(m_data.chargeTypeId);
|
|
aiv2.effectStart(toMSec(m_data.effectStart));
|
|
aiv2.form().clear();
|
|
aiv2.form().reserve(m_triggeredMines.size());
|
|
for (const auto &tm : qAsConst(m_triggeredMines))
|
|
aiv2.form().resize(aiv2.form().size() + 1, Core::GeoPosition(tm.position.x(), tm.position.y(), 0.0));
|
|
aiv2.boundingBoxBottomLeft(aiv.boundingBoxBottomLeft());
|
|
aiv2.boundingBoxTopRight(aiv.boundingBoxTopRight());
|
|
aiv2.aesTypeId(AesType::inner_enum::AtMineAreaSingleMines);
|
|
aiv2.id(1000 * m_data.id + AesType::inner_enum::AtMineAreaSingleMines);
|
|
aiv2.effectStateId(m_data.effectStateId);
|
|
if (m_data.partyId != 0)
|
|
aiv2.partyId(m_data.partyId);
|
|
|
|
if (aiv2 != m_aiv2 || m_triggeredMinesChanged)
|
|
{
|
|
DEBUG_LOG(m_logger) << "aiv2 changed";
|
|
aiv2.sampleTime(partitionTimeMSec());
|
|
m_aiv2 = aiv2;
|
|
m_mainHandler->dds()->dts()->publish(aiv2);
|
|
emit newAiv(aiv2);
|
|
}
|
|
}
|
|
|
|
void ObjectMineArea::removeTopics()
|
|
{
|
|
DEBUG_LOG(m_logger) << "removeTopics()";
|
|
|
|
auto dts = m_mainHandler->dds()->dts();
|
|
dts->removeMineAreaByKey(m_data.id);
|
|
dts->removeAesItemViewByKey(1000 * m_data.id + AesType::inner_enum::AtMineArea);
|
|
dts->removeAesItemViewByKey(1000 * m_data.id + AesType::inner_enum::AtMineAreaSingleMines);
|
|
dts->removeTriggeredImpactsByKey(1000 * m_data.id + AesType::inner_enum::AtMineAreaSingleMines);
|
|
removeDusMineStateTopic();
|
|
emit removeAiv(m_data.id);
|
|
}
|
|
/*!
|
|
* \brief ObjectMineArea::createSegmentsFromMineData
|
|
* \param data: Data width will be actualized after the new rect is determined
|
|
* \param mineData: are the mines pos data tha need to be added to the existing mine area
|
|
* \param outSegments: size is 2 corresponding to the lenght of the rect. width is derived from Area(MneData + triggerMines)
|
|
* \return
|
|
*/
|
|
Core::FunctionReplyObject ObjectMineArea::createGeoSegmentsFromMineData(double &newWidth,
|
|
const std::vector<MineData> &mineData,
|
|
std::vector<Core::GeoPosition2D> &outSegments)
|
|
{
|
|
Core::FunctionReplyObject ret;
|
|
// reconstruct GeoPos segment from mineData + triggeredMine // DusMineState is derived from triggeredMine
|
|
// 1. create a mine vector from mineData + m_triggeredMines
|
|
// 2. get rectangle from mine vector
|
|
// 3. build segment from it
|
|
|
|
std::vector<Core::GeoPosition2D> tempMinePos;
|
|
tempMinePos.reserve(m_triggeredMines.size() + mineData.size());
|
|
for (const auto &t : qAsConst(m_triggeredMines))
|
|
tempMinePos.emplace_back(Core::GeoPosition2D(t.position.x(), t.position.y()));
|
|
|
|
for (const auto &md : mineData)
|
|
tempMinePos.emplace_back(md.position());
|
|
|
|
DEBUG_LOG(m_logger) << "--[createSegmentsFromMineData]: --> tempMinePos.Size: " << tempMinePos.size() << " m_triggeredMinesSIze: " << m_triggeredMines.size()
|
|
<< " mineDataSize " << mineData.size();
|
|
if (tempMinePos.empty())
|
|
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Mine data couldn't be added"));
|
|
|
|
LtsCoordTrans::UtmCoordinate tempCoord = LtsCoordTrans::UtmCoordinate::fromGeographic(tempMinePos.front().lon(), tempMinePos.front().lat());
|
|
|
|
// calculate position
|
|
double angleDeg = 0.0;
|
|
const QVector<QPointF> points = Polygons::geoVecToUtmPointF(tempMinePos);
|
|
QRectF areaRect = Polygons::getMinAreaRect(points, angleDeg);
|
|
|
|
QPointF bottomRight(areaRect.width(), 0);
|
|
QTransform trans;
|
|
trans.rotate(-angleDeg);
|
|
auto endButtomRight = trans.map(bottomRight);
|
|
LtsCoordTrans::UtmCoordinate coordBr(tempCoord.zone, endButtomRight.x() + areaRect.bottomLeft().x(), endButtomRight.y() + areaRect.bottomLeft().y());
|
|
|
|
QPointF topRight(0, areaRect.height());
|
|
auto endTopRight = trans.map(topRight);
|
|
LtsCoordTrans::UtmCoordinate coordTr(tempCoord.zone, endTopRight.x() + areaRect.bottomLeft().x(), endTopRight.y() + areaRect.bottomLeft().y());
|
|
|
|
QPointF topleft(areaRect.width(), areaRect.height());
|
|
auto endTopleft = trans.map(topleft);
|
|
LtsCoordTrans::UtmCoordinate coordTl(tempCoord.zone, endTopleft.x() + areaRect.bottomLeft().x(), endTopleft.y() + areaRect.bottomLeft().y());
|
|
|
|
LtsCoordTrans::UtmCoordinate coordBl(tempCoord.zone, areaRect.bottomLeft().x(), areaRect.bottomLeft().y());
|
|
|
|
DEBUG_LOG(m_logger) << "--[createSegmentsFromMineData] AREA HEIGHT(newWidth): " << areaRect.height() << " oldWidth: " << m_data.width;
|
|
newWidth = areaRect.height();
|
|
// anscheint darstellen beginnt von mitte
|
|
|
|
QPointF centerTopleft(areaRect.width(), areaRect.height() / 2.0);
|
|
auto transTopleft = trans.map(centerTopleft);
|
|
LtsCoordTrans::UtmCoordinate coordCenterTl(tempCoord.zone, transTopleft.x() + areaRect.bottomLeft().x(), transTopleft.y() + areaRect.bottomLeft().y());
|
|
QPointF centerTopRight(0, areaRect.height() / 2.0);
|
|
auto transTopRight = trans.map(centerTopRight);
|
|
LtsCoordTrans::UtmCoordinate coordCenterTr(tempCoord.zone, transTopRight.x() + areaRect.bottomLeft().x(), transTopRight.y() + areaRect.bottomLeft().y());
|
|
|
|
outSegments.reserve(2); // to generate segment from rect 2 point are needed (width is known)
|
|
outSegments.emplace_back(Core::GeoPosition2D(coordCenterTl.geographic().x(), coordCenterTl.geographic().y()));
|
|
outSegments.emplace_back(Core::GeoPosition2D(coordCenterTr.geographic().x(), coordCenterTr.geographic().y()));
|
|
|
|
DEBUG_LOG(m_logger) << " ------ [createSegmentsFromMineData] geo bl: " << Core::GeoPosition2D(coordBl.geographic().x(), coordBl.geographic().y());
|
|
DEBUG_LOG(m_logger) << " ------ [createSegmentsFromMineData] geo br: " << Core::GeoPosition2D(coordBr.geographic().x(), coordBr.geographic().y());
|
|
DEBUG_LOG(m_logger) << " --**-- [createSegmentsFromMineData] geo tl: " << Core::GeoPosition2D(coordTl.geographic().x(), coordTl.geographic().y());
|
|
DEBUG_LOG(m_logger) << " --**-- [createSegmentsFromMineData] geo tr: " << Core::GeoPosition2D(coordTr.geographic().x(), coordTr.geographic().y());
|
|
DEBUG_LOG(m_logger) << " --**-- [createSegmentsFromMineData] geo ctl: " << Core::GeoPosition2D(coordCenterTl.geographic().x(), coordCenterTl.geographic().y());
|
|
DEBUG_LOG(m_logger) << " --**-- [createSegmentsFromMineData] geo ctr: " << Core::GeoPosition2D(coordCenterTr.geographic().x(), coordCenterTr.geographic().y());
|
|
|
|
return Core::FunctionReplyObject(m_data.id);
|
|
}
|
|
|
|
void ObjectMineArea::onEffectStateChanged()
|
|
{
|
|
DEBUG_LOG(m_logger) << id() << " EffectStateChanged for MineArea " << m_data.id << ". Check if mines have to be released.";
|
|
// If is an DUS-MineArea and it's state is Expired or Removed and still has mines
|
|
if ((isDusMineArea() || isConcreteMine())
|
|
&& (m_data.effectStateId.underlying() == AesEffectState::EsExpired || m_data.effectStateId.underlying() == AesEffectState::EsRemoved)
|
|
&& !m_triggeredMines.isEmpty())
|
|
{
|
|
DEBUG_LOG(m_logger) << id() << " Releasing all mines from " << toString(m_data.effectStateId) << " MineArea " << m_data.id;
|
|
emit releaseMines(m_data.id, m_triggeredMines.values());
|
|
m_triggeredMines.clear();
|
|
}
|
|
}
|
|
|
|
QPolygonF ObjectMineArea::generateForm()
|
|
{
|
|
DEBUG_LOG(m_logger) << "generateForm()";
|
|
return Polygons::mineArea(m_segments, m_data.width, m_data.density, m_data.isCenterLine);
|
|
}
|
|
|
|
// get startPos of all mines (nearest to the smaller side of the polygon)
|
|
int ObjectMineArea::msGetIndexMine(const QVector<LtsCoordTrans::UtmCoordinate> &box, const QVector<LtsCoordTrans::UtmCoordinate> &minen)
|
|
{
|
|
double dist;
|
|
double dist0;
|
|
double dist1;
|
|
double dx0;
|
|
double dy0;
|
|
double dx1;
|
|
double dy1;
|
|
double px;
|
|
double py;
|
|
double px0;
|
|
double py0;
|
|
double px1;
|
|
double py1;
|
|
int idx;
|
|
|
|
dx0 = box.at(1).x - box.at(0).x;
|
|
dy0 = box.at(1).y - box.at(0).y;
|
|
dist0 = sqrt(dx0 * dx0 + dy0 * dy0);
|
|
dx1 = box.at(2).x - box.at(1).x;
|
|
dy1 = box.at(2).y - box.at(1).y;
|
|
dist1 = sqrt(dx1 * dx1 + dy1 * dy1);
|
|
if (dist0 < dist1)
|
|
{
|
|
px0 = box.at(1).x;
|
|
py0 = box.at(1).y;
|
|
px1 = box.at(0).x;
|
|
py1 = box.at(0).y;
|
|
}
|
|
else
|
|
{
|
|
px0 = box.at(2).x;
|
|
py0 = box.at(2).y;
|
|
px1 = box.at(1).x;
|
|
py1 = box.at(1).y;
|
|
}
|
|
|
|
px = minen.at(0).x;
|
|
py = minen.at(0).y;
|
|
dist = msGetDistanceFromLine(px, py, px0, py0, px1, py1);
|
|
idx = 0;
|
|
for (int i = 0; i < minen.size(); i++)
|
|
{
|
|
px = minen.at(i).x;
|
|
py = minen.at(i).y;
|
|
if (dist > msGetDistanceFromLine(px, py, px0, py0, px1, py1))
|
|
{
|
|
dist = msGetDistanceFromLine(px, py, px0, py0, px1, py1);
|
|
idx = i;
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
double ObjectMineArea::msGetDistanceFromLine(double px, double py, double px0, double py0, double px1, double 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 fabs(dist);
|
|
}
|
|
|
|
} // Namespace AreaEffectSimulation
|