/** * 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 #include #include 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(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId)) == MineClass::DUS; } bool ObjectMineAreaNull::isConcreteMine() const { return static_cast(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId)) == MineClass::Concrete; } bool ObjectMineAreaNull::isVirtualSingleMine() const { return static_cast(m_mainHandler->dds()->pdmDtc()->mineClass(m_data.chargeTypeId, m_data.igniterTypeId, m_data.igniterFunctionId)) == MineClass::VirtualSingle; } bool ObjectMineAreaNull::isVirtualMine() const { return static_cast(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 &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 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(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(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(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 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