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

340 lines
9.6 KiB
C++
Executable File

/**
* Object for the null mine area (where the unassigned DUS mines belong to)
* $Id$
* $HeadURL$
* $Author$
*/
#include "object_mine_area_null.h"
#include "special_area_provider.h"
#include <QPolygonF>
#include <LtsUtilsCore/dds_bitarray.h>
#include <ngldb/dbtransaction.h>
namespace AreaEffectSimulation
{
ObjectMineAreaNull::ObjectMineAreaNull(MainHandler *mainHandler,
QObject *parent)
: ObjectBaseTmpl(mainHandler, QStringLiteral("MineAreaNull"), parent)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_setId(0)
{
// 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, 0)) // mineArea 0
{
ERROR_LOG(m_logger) << "Error reading data from database!";
}
// Set TriggeredExplosions Base Data
m_tis.id(AesType::AtMineAreaSingleMines);
m_tis.aesItemId(0);
m_tis.aesTypeId(AesType::AtMineAreaSingleMines);
// Set DUS-Mine status Base Data
m_dms.mines().resize(m_triggeredMines.size());
onPublishData();
}
ObjectMineAreaNull::~ObjectMineAreaNull()
{
removeTopics();
}
bool ObjectMineAreaNull::isDusMineArea() const
{
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
== MineClass::DUS;
}
bool ObjectMineAreaNull::isConcreteMine() const
{
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
== MineClass::Concrete;
}
bool ObjectMineAreaNull::isVirtualSingleMine() const
{
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
== MineClass::VirtualSingle;
}
bool ObjectMineAreaNull::isVirtualMine() const
{
return static_cast<MineClass::type>(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId))
== MineClass::Virtual;
}
int ObjectMineAreaNull::lastConcreteMineNo()
{
int lastMineNo = 0;
for (const Database::AesMineAreaTriggeredMines &mine : qAsConst(m_triggeredMines))
{
// NOTE: igniterType/IgniterFunciton = 0 as long as AesMineAreaTriggeredMines does not contain values
DEBUG_LOG(m_logger) << "mine: chargeTypeId " << mine.chargeTypeId << ", mineId: " << mine.mineId;
if (isConcreteMine() ||
isDusMineArea())
{
if (mine.mineId > lastMineNo)
lastMineNo = mine.mineId;
}
}
return lastMineNo;
}
bool ObjectMineAreaNull::triggerMine(int mineId, bool trigger, int chargeTypeId, const QPointF &position, bool publish)
{
Database::AesMineAreaTriggeredMines &tm = m_triggeredMines[mineId];
tm.mineAreaId = 0;
tm.mineId = mineId;
tm.isTriggered = trigger;
if (chargeTypeId > 0)
tm.chargeTypeId = chargeTypeId;
if (trigger)
tm.triggerTime = QDateTime::fromMSecsSinceEpoch(partitionTimeMSec());
if (!position.isNull())
tm.position = position;
bool bOk = true;
DEBUG_LOG(m_logger) << "tm.id: " << tm.id << ", mineId: " << tm.mineId << ", mineAreaId: " << tm.mineAreaId;
if (tm.chargeTypeId == 0)
{
DEBUG_LOG(m_logger) << "Not possible to trigger mine with invalid chargeTypeId 0";
return false;
}
if (!tm.id)
{
bOk &= m_mainHandler->db()->insert(tm);
m_setId++;
}
else
bOk &= m_mainHandler->db()->update(tm);
if (!bOk)
return false;
if (publish)
onPublishData();
return true;
}
bool ObjectMineAreaNull::triggerMine(Database::AesMineAreaTriggeredMines &mine)
{
DEBUG_LOG(m_logger) << "Triggering mine " << mine.mineId << " with dbId " << mine.id;
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 ObjectMineAreaNull::triggerMines(const QList<Database::AesMineAreaTriggeredMines> &mines)
{
DBTransaction trans(m_mainHandler->db()->database());
bool bOk = true;
for (const auto &mine : mines)
{
m_triggeredMines[mine.mineId] = mine;
m_triggeredMines[mine.mineId].mineAreaId = 0;
if (!m_mainHandler->db()->update(m_triggeredMines[mine.mineId]))
{
bOk = false;
ERROR_LOG (m_logger) << "Error updating mine " << mine.mineId << " in db!";
}
}
if (bOk)
trans.commit();
m_setId++;
onPublishData();
}
bool ObjectMineAreaNull::resetMine(int mineId)
{
return triggerMine(mineId, false);
}
bool ObjectMineAreaNull::resetMines()
{
DEBUG_LOG(m_logger) << "Resetting unassociated DUS mines, count: " << m_triggeredMines.size();
bool bOk = true;
auto it = m_triggeredMines.begin();
while (it != m_triggeredMines.end())
{
Database::AesMineAreaTriggeredMines &tm = (*it);
if (tm.isTriggered)
{
DEBUG_LOG(m_logger) << "Reset mine with id " << tm.mineId;
tm.isTriggered = false;
bOk &= m_mainHandler->db()->update(tm);
}
++it;
}
if (!bOk)
return false;
// This pauses the TAB reaction on mine reset changes...
const TrainingAreaBase::SendResetAllDusMinesRequest req;
m_requestHelper->startSingleRequest([req, this]()
{
return m_mainHandler->dds()->tabFtc()->sendResetAllDusMinesRequest(req,this);
});
// We're publishing data anyhow. So do the other DUS mine areas.
onPublishData();
return true;
}
Database::AesMineAreaTriggeredMines ObjectMineAreaNull::takeMine(int mineId)
{
if (!m_triggeredMines.contains(mineId))
return Database::AesMineAreaTriggeredMines();
Database::AesMineAreaTriggeredMines ret = m_triggeredMines.value(mineId);
m_triggeredMines.remove(mineId);
m_setId++;
onPublishData();
return ret;
}
void ObjectMineAreaNull::onPublishData()
{
DDSConnector::CoherentSet cs (m_mainHandler->dds()->ddsConnector());
ObjectBaseTmpl::onPublishData();
publishTriggeredImpacts();
publishDusMineStatus();
publishAiv();
}
void ObjectMineAreaNull::removeTopics()
{
auto dts = m_mainHandler->dds()->dts();
dts->removeMineAreaByKey(m_data.id);
dts->removeAesItemViewByKey(1000 * m_data.id + AesType::AtMineAreaSingleMines);
dts->removeTriggeredImpactsByKey(1000 * m_data.id + AesType::AtMineAreaSingleMines);
emit removeAiv(m_data.id);
}
void ObjectMineAreaNull::publishAiv()
{
// Todo: calculate unique id.
// Better than aiv.id(1000 * aesItemId + AesType::AtMineArea);
std::vector<Core::GeoPosition> vec;
vec.reserve(m_triggeredMines.size());
QPolygonF tmp;
tmp.reserve(m_triggeredMines.size());
for (const auto &mine : qAsConst(m_triggeredMines))
{
vec.emplace_back(Core::GeoPosition(mine.position.x(), mine.position.y(), 0));
tmp.push_back(mine.position);
}
AesItemView aiv2(m_aiv2);
aiv2.aesItemId(0);
aiv2.chargeStruct().chargeTypeId(m_data.chargeTypeId); // Shall be invalid. EffectSimulation has been conducted before, so not necessary for service sCES
aiv2.effectStart(0);
aiv2.form(vec);
aiv2.boundingBoxBottomLeft() = Core::GeoPosition2D(tmp.boundingRect().bottomLeft().x(), tmp.boundingRect().bottomLeft().y());
aiv2.boundingBoxTopRight() = Core::GeoPosition2D(tmp.boundingRect().topRight().x(), tmp.boundingRect().topRight().y());
aiv2.aesTypeId(AesType::inner_enum::AtMineAreaSingleMines);
aiv2.id(AesType::inner_enum::AtMineAreaSingleMines);
aiv2.effectStateId(AesEffectState::inner_enum::EsActivated);
aiv2.setId(dds::core::optional<rti::core::int64>(m_setId));
if (m_data.partyId != 0)
aiv2.partyId(m_data.partyId);
checkIsCentralAndPublish(aiv2.form(), m_aiv2.form(), m_data.isCentral);
if (aiv2 != m_aiv2)
{
aiv2.sampleTime(partitionTimeMSec());
m_aiv2 = aiv2;
m_mainHandler->dds()->dts()->publish(aiv2);
emit newAiv(aiv2);
}
}
bool ObjectMineAreaNull::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);
tis.setId(dds::core::optional<rti::core::int64>(m_setId));
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_tis = tis;
m_tis.sampleTime(partitionTimeMSec());
return m_mainHandler->dds()->dts()->publish(m_tis);
}
return true;
}
bool ObjectMineAreaNull::publishDusMineStatus()
{
// DusMineStatus mines vector is coherent to triggered impacts bitfield
DusMineState dms(m_dms);
dms.mines().resize(m_triggeredMines.size());
dms.setId(dds::core::optional<rti::core::int64>(m_setId));
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_dms = dms;
m_dms.sampleTime(partitionTimeMSec());
return m_mainHandler->dds()->dts()->publish(m_dms);
}
return true;
}
QPolygonF ObjectMineAreaNull::generateForm()
{
std::vector<Core::GeoPosition> vec;
vec.reserve(m_triggeredMines.size());
QPolygonF tmp;
tmp.reserve(m_triggeredMines.size());
for (const auto &mine : qAsConst(m_triggeredMines))
{
vec.emplace_back(Core::GeoPosition(mine.position.x(), mine.position.y(), 0));
tmp.push_back(mine.position);
}
return tmp;
}
} // namespace