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

923 lines
38 KiB
C++
Executable File

/**
* @RDE_HEADER@
* Handler for all Effect Cluster
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLSE, 2023-10
*/
#include "handler_effect_cluster.h"
#include "handler_charge_administration.h"
#include "object_effect_cluster.h"
#include "settings.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QPolygonF>
#include <ProjectDataManagement/pdm_translation.h>
#include <Services/AreaEffectSimulation/Utils/polygons.h>
#include <TrainingAreaBase/tab_translation.h>
// for TrajectoryGenerator
#include <AreaEffectSimulation/handler_charge_effect.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <Proxies/TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
namespace AreaEffectSimulation
{
HandlerEffectCluster::HandlerEffectCluster(MainHandler *mainHandler)
: HandlerBaseTmpl<ObjectEffectCluster>(mainHandler,
QStringLiteral("EffectCluster"),
s_areaCount[AesType::AtEffectCluster],
s_areaCountCentral[AesType::AtEffectCluster])
, Interface::Fts::EffectClusterModifyRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EffectClusterModifyActorRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EffectClusterSingleEffectOnlyModifyRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EffectClusterOcModifyRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EffectClusterRemoveRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EffectClusterPauseResumeRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EffectClusterFireHaltRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::TrajectoryAdlerModifyRequestHandler(m_mainHandler->dds()->fts())
, Interface::Fts::EffectClusterReleaseReceivedRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::AcknowledgeExternalAesItemRequestHandler(m_mainHandler->dds()->fts())
, Interface::Fts::SetAesItemEffectorStateRequestHandler(m_mainHandler->dds()->fts())
{
}
bool HandlerEffectCluster::init()
{
// create explosion cluster objects
QVector<Database::AesEffectorData> effectorVec;
bool bRet = true;
if (!m_mainHandler->db()->select(effectorVec))
{
ERROR_LOG(m_logger) << "Error Reading Effector Data from Database";
return false;
}
auto cb = [this, &bRet, effectorVec](const Database::AesEffectCluster &data)
{
DEBUG_LOG(m_logger) << "my vector has:" << effectorVec.size() << " my aesData " << data.id;
// Add EffectorData to Object
QVector<Database::AesEffectorData> effectorDataVec;
for (const auto &effectorData : effectorVec)
{
DEBUG_LOG(m_logger) << "INIT TRY ADD effector with entity ID " << effectorData.entityId << " with aesData " << data.id;
if (effectorData.entityId == data.id)
effectorDataVec.append(effectorData);
}
ObjectEffectCluster *ifs(new ObjectEffectCluster(m_mainHandler, this));
connect(ifs, &ObjectEffectCluster::effectStateChanged, this, &HandlerEffectCluster::onEffectStateChanged);
auto data_copy = data;
if (data.impactCount == 0)
{
for (const auto &effector : effectorDataVec)
{
data_copy.impactCount += effector.chargeAmount;
}
}
Core::FunctionReplyObject ret = ifs->createObject(data_copy, effectorDataVec);
if (ret.error())
{
delete ifs;
ERROR_LOG(m_logger) << ret();
bRet = false;
return true;
}
// Context NASAT: check via chargeTypeId:
const bool bChargeIsAirSupport = m_mainHandler->dds()->pdmDtc()->gearClassFromChargeTypeId(ifs->data().chargeTypeId) == ProjectDataManagement::GearClass::AirSupport;
ifs->setAirSupportSource(bChargeIsAirSupport);
// Charge Effects are alwazs Central!
if (bChargeIsAirSupport)
{
// Note: s_currentAirSupportCount is updated via overloaded recountActiveAreas
if (s_currentAirSupportCount >= s_maxAirSupportCount)
{
ERROR_LOG(m_logger) << "More active air supports in database than allowed - ignoring the rest!";
ifs->removeObject();
delete ifs;
return true;
}
}
if (ifs->data().isCentral)
++m_areaCountCentral;
m_objectMap.insert(ifs->id(), ifs);
connect(ifs, &ObjectEffectCluster::newAiv, this, &HandlerEffectCluster::newAiv);
connect(ifs, &ObjectEffectCluster::removeAiv, this, &HandlerEffectCluster::removeAiv);
const QJsonObject propertiesObject = QJsonDocument::fromJson(QByteArray::fromStdString(ifs->data().properties.toStdString())).object();
m_referenceToClusterId.insert(propertiesObject[AdlerJson::JsonKeys::FEUERKAMPFREFERENZ()].toString(), ifs->id());
return true;
};
if (!m_mainHandler->db()->select(cb) || !bRet)
{
ERROR_LOG(m_logger) << "Error reading data from database!";
return false;
}
DEBUG_LOG(m_logger) << "m_exeObjMap.size() = " << m_objectMap.size();
DEBUG_LOG(m_logger) << "m_areaCountCentral = " << m_areaCountCentral;
// init TAB
auto dtcTab = m_mainHandler->dds().get()->tabDtc().get();
if (!dtcTab->isEX_CeaseFiringEnabled())
dtcTab->enableEX_CeaseFiring();
if (!dtcTab->isEX_FireEnabled())
dtcTab->enableEX_Fire();
connect(dtcTab, &TrainingAreaBase::Interface::Dtc::eXFireChanged, this, &HandlerEffectCluster::onFireCommandReceived);
connect(dtcTab, &TrainingAreaBase::Interface::Dtc::eXCeaseFiringChanged, this, &HandlerEffectCluster::onFireHoldReceived);
return true;
}
const ObjectEffectCluster *HandlerEffectCluster::getEffectClusterObj(int id) { return m_objectMap.value(id, nullptr); }
void HandlerEffectCluster::onFireCommandReceived(const TrainingAreaBase::EX_Fire &fire, DataModification modtype)
{
// get fireReference
if (modtype == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "got EX_Fire for ref: " << fire.reference() << " internal mapsize " << m_referenceToClusterId.size();
auto *ifs = m_objectMap.value(m_referenceToClusterId.value(QString::fromStdString(fire.reference())));
if (!ifs)
return;
ifs->sendFireCommand(AdlerJson::waffensystemkennerMap.key(fire.effectorId()));
}
void HandlerEffectCluster::onFireHoldReceived(const TrainingAreaBase::EX_CeaseFiring &ceaseFire, DataModification modType)
{
Q_UNUSED(ceaseFire);
if (modType == DataModification::eRemoved)
return;
}
int HandlerEffectCluster::totalAreasCentral() const { return s_areaCountCentral[AesType::AtEffectCluster]; }
// Appends
/*
static void updateProperties(Database::AesEffectCluster &dst, QVector<EffectorData> &dstEffectorVector)
{
QJsonObject propertiesObject = QJsonDocument::fromJson(QByteArray::fromStdString(dst.properties.toStdString())).object();
propertiesObject[QStringLiteral("EffectorData")] = dstEffectorVector.size();
QJsonDocument doc(propertiesObject);
dst.properties = QString::fromStdString(doc.toJson(QJsonDocument::Compact).toStdString());
}
*/
static void fillData(const EffectClusterModifyRequest &src,
Database::AesEffectCluster &dst,
QVector<Database::AesEffectorData> &dstEffectorVector,
MainHandler *mainHandler)
{
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.element = QString::fromStdString(src.element());
dst.impactPosition.rx() = src.area().cornerBottomLeft().lon();
dst.impactPosition.ry() = src.area().cornerBottomLeft().lat();
dst.impactLength = src.area().height();
dst.impactWidth = src.area().width();
dst.rotationAngle = src.area().rotation();
const bool bIsPointSimilar = (src.explosionGeometry() == AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint)
|| (src.explosionGeometry() == AreaEffectSimulation::AesExplosionGeometry_def::EgPoint);
dstEffectorVector.reserve(src.effectors().size());
Database::AesEffectorData dstEffector;
int localImpactCount = 0;
int firstChargeTypeId = 0;
for (const auto &effector : src.effectors())
{
// copy from Source Request.
dstEffector.angleOfFall = effector.angleOfFall();
dstEffector.chargeAmount = effector.chargeAmount();
dstEffector.chargeTypeId = effector.chargeStruct().chargeTypeId();
dstEffector.entityId = src.id();
dstEffector.flightTime = effector.flightTime();
dstEffector.igniterTypeId = effector.chargeStruct().igniterTypeId();
dstEffector.igniterFunctionId = effector.chargeStruct().igniterFunctionId();
dstEffector.interval = effector.interval();
dstEffector.mhpPosition.rx() = effector.mhpPosition().lon();
dstEffector.mhpPosition.ry() = effector.mhpPosition().lat();
dstEffector.mhpPosition.rz() = effector.mhpPosition().z(); // [E01562-399]
dstEffector.properties = QString::fromStdString(effector.properties());
// FillScatterData
if (effector.mhpScatterLenght() > 0.0)
dstEffector.mhpScatterLenght = effector.mhpScatterLenght();
else
dstEffector.mhpScatterLenght = Settings::getScatterLenghtDefault();
dstEffector.mhpScatterLengthDetonationHeight = effector.mhpScatterLengthDetonationHeight();
if (effector.mhpScatterWidth() > 0.0)
dstEffector.mhpScatterWidth = effector.mhpScatterWidth();
else
dstEffector.mhpScatterWidth = Settings::getScatterWidthDefault();
dstEffector.mhpScatterWidthDetonationHeight = effector.mhpScatterWidthDetonationHeight();
// Fill rest
dstEffector.participantId = effector.participantId();
dstEffector.peakHeight = effector.peakHeight();
dstEffector.trajectory = QString::fromStdString(TrajectoryRelative::toString(effector.trajectoryRelative()));
// sanity stuff
localImpactCount = localImpactCount + dstEffector.chargeAmount;
if (firstChargeTypeId <= 0)
firstChargeTypeId = effector.chargeStruct().chargeTypeId();
// check if it's a single fire
if (bIsPointSimilar && ((src.area().width() <= 0) || (src.area().height() <= 0)))
{
const Core::GeoPosition2D &cornerBottomLeft = src.area().cornerBottomLeft();
const double impactDiameter = mainHandler->dds()->pdmDtc()->impactRadius(effector.chargeStruct().chargeTypeId(),
effector.chargeStruct().igniterTypeId(),
effector.chargeStruct().igniterFunctionId())
* 2.0; // GUEZE-2634
const QPointF bottomLeft(Polygons::centerPoint2BottomLeft(QPointF(cornerBottomLeft.lon(), cornerBottomLeft.lat()), impactDiameter, impactDiameter, 0));
dstEffector.mhpPosition.rx() = bottomLeft.x();
dstEffector.mhpPosition.ry() = bottomLeft.y();
dstEffector.mhpScatterLenght = impactDiameter;
dstEffector.mhpScatterWidth = impactDiameter;
}
dstEffectorVector.append(dstEffector);
}
// Save the mhpCountRequest for later
dst.mhpCountRequest = dstEffectorVector.size();
dst.explosionGeometryId = src.explosionGeometry();
dst.tla = src.tla();
dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
// Effect Duration might be set and will be used for the AutoFire
// -- Module Duration
if (src.effectDuration())
dst.effectDuration = src.effectDuration();
else
{
// Sometimes there is no effectDuration
// We use a more or less uniqe duration for now
dst.effectDuration = DEFAULT_EFFECT_DURATION;
}
dst.effectSourceId = src.effectSourceId();
dst.firePurposeId = src.firePurposeId();
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
dst.participantId = src.participantId().is_set() ? src.participantId().get() : -1; // -1 -> avoid trajectory
dst.altitude = -1;
dst.effectMarkingInTa = src.effectMarkingInTa();
dst.externalId = src.externalId().is_set() ? src.externalId().get() : 0;
dst.properties = QString::fromStdString(src.properties());
dst.esmParticipantIds = LtsUtils::IntArray(src.esmParticipantIds());
// some modules:
// if (dst.impactCount == 0 && localImpactCount > 0)
dst.impactCount = localImpactCount;
if (dst.chargeTypeId == 0 && firstChargeTypeId > 0)
dst.chargeTypeId = firstChargeTypeId;
// update Properties
// updateProperties(dst, dstEffectorVector);
}
void HandlerEffectCluster::onEffectClusterModifyRequest(const rti::core::SampleIdentity &cookie, const EffectClusterModifyRequest &request)
{
// DEBUG_LOG(m_logger) << "************************************ start onEffectClusterModifyRequest: " << request;
Core::FunctionReplyObject ret = effectClusterModifyRequest(cookie, request);
if (ret.error())
WARNING_LOG(m_logger) << ret() << ", effectStart :" << request.effectStart();
ret.setAdditionalInfo(request.properties().c_str());
// DEBUG_LOG(m_logger) << "************************************ SEND REPLY FOR onEffectClusterModifyRequest: " << request;
m_mainHandler->dds()->fts()->effectClusterModifyReply(cookie, ret);
}
void HandlerEffectCluster::onEffectClusterModifyActorRequest(const rti::core::SampleIdentity &cookie, const EffectClusterModifyActorRequest &request)
{
// DEBUG_LOG(m_logger) << "************************************ start onEffectClusterModifyActorRequest: " << request;
Core::FunctionReplyObject ret;
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, request.aesId(), ret);
if (!ifs)
{
m_mainHandler->dds()->fts()->effectClusterModifyActorReply(cookie, ret);
return;
}
ret = ifs->effectClusterModifyActor(request);
if (ret.error())
ERROR_LOG(m_logger) << ret();
DEBUG_LOG(m_logger) << "************************************ SEND REPLY FOR onEffectClusterModifyActorRequest: " << request;
m_mainHandler->dds()->fts()->effectClusterModifyActorReply(cookie, ret);
}
void HandlerEffectCluster::onEffectClusterSingleEffectOnlyModifyRequest(const rti::core::SampleIdentity &cookie, const EffectClusterSingleEffectOnlyRequest &request)
{
DEBUG_LOG(m_logger) << "************************************ onEffectClusterSingleEffectModifyRequest: " << request;
// generalize the specific request to the common one. (similar an 'upcast')
// in other words: EffectClusterSingleEffectOnlyRequest -> EffectClusterModifyRequest:
EffectClusterModifyRequest ecCommonModifyRequest;
/*
ecCommonModifyRequest.id(request.id());
ecCommonModifyRequest.externalId(request.externalId());
ecCommonModifyRequest.effectStart(request.effectStart());
ecCommonModifyRequest.effectDuration(1000);
// charge and geometry:
ecCommonModifyRequest.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint);
// ecCommonModifyRequest.zog(0);
ecCommonModifyRequest.fuseType(ProjectDataManagement::FuseType_def::FtImpact);
ecCommonModifyRequest.chargeTypeId(request.chargeTypeId());
// height, width are <=0 -> area will be adjusted using impactRadius of charge:
ecCommonModifyRequest.area().cornerBottomLeft(request.impactPosition());
ecCommonModifyRequest.area().height(0);
ecCommonModifyRequest.area().width(0);
ecCommonModifyRequest.mhpCount(1);
ecCommonModifyRequest.impactCount(1);
ecCommonModifyRequest.name(request.name()); // ZpNummer
ecCommonModifyRequest.element(request.element()); // element
ecCommonModifyRequest.partyId(request.partyId());
ecCommonModifyRequest.participantId(-1); // -1 -> avoid trajectory
*/
Core::FunctionReplyObject ret = effectClusterModifyRequest(cookie, ecCommonModifyRequest);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->effectClusterSingleEffectOnlyModifyReply(cookie, ret);
}
void HandlerEffectCluster::onEffectClusterOcModifyRequest(const rti::core::SampleIdentity &cookie, const EffectClusterOcModifyRequest &request)
{
DEBUG_LOG(m_logger) << "************************************ onEffectClusterOcModifyRequest: " << request;
// generalize the specific request to the common one. (similar an 'upcast')
// in other words: EffectClusterSingleEffectOnlyRequest -> EffectClusterModifyRequest:
EffectClusterModifyRequest ecCommonModifyRequest;
ecCommonModifyRequest.id(request.id());
// ecCommonModifyRequest.externalId(request.externalId());
ecCommonModifyRequest.effectStart(request.effectStart());
ecCommonModifyRequest.effectDuration(request.effectDuration());
// charge and geometry:
ecCommonModifyRequest.area(request.area());
int impactCount = 0;
for (const auto &effector : request.effectors())
effector.chargeAmount();
if (impactCount == 1)
ecCommonModifyRequest.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint);
else
ecCommonModifyRequest.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry_def::EgArea);
// ecCommonModifyRequest.zog(0);
ecCommonModifyRequest.effectors(request.effectors());
ecCommonModifyRequest.name(request.name()); // ZpNummer
ecCommonModifyRequest.element(ecCommonModifyRequest.element()); // element
ecCommonModifyRequest.partyId(request.partyId());
// ecCommonModifyRequest.participantId(-1); // -1 -> avoid trajectory
// ToDo: no trajectory needed, but how can we transfer the rotation of MhpAreas?
// ToDo: no trajectory needed, but how can we transfer the rotation of MhpAreas?
// ToDo: no trajectory needed, but how can we transfer the rotation of MhpAreas?
// ToDo: how to transfer request.mhpRotation??
Core::FunctionReplyObject ret = effectClusterModifyRequest(cookie, ecCommonModifyRequest);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->effectClusterOcModifyReply(cookie, ret);
}
Core::FunctionReplyObject HandlerEffectCluster::effectClusterCreateRequest(const EffectClusterModifyRequest &request)
{
// HLA Interface is using externalId...
// external (externalId>0) -> no partyId required
// external (externalId==0) -> partyId required
// We keep the old charge behavior
// and also ignore it from ADLER?
const int externalIdValue = request.externalId().is_set() ? request.externalId().get() : 0;
if (request.effectors().size() == 0)
return Core::FunctionReplyObject(tr_lts("dds", "Invalid Effector Count"));
// We assume that there is no mixture of GeraClasses between charges
const ProjectDataManagement::GearClass gearClass = m_mainHandler->gearClassOfChargeTypeId(request.effectors().at(0).chargeStruct().chargeTypeId());
const bool bDoClaimPlannedCharge = (request.id() == 0) && (externalIdValue == 0)
&& (gearClass != ProjectDataManagement::GearClass_def::AirSupport
//&& (m_mainHandler->dds()->pdmDtc()->isAdlerChargeType(request.chargeTypeId()))
)
&& 1 == 2 && (request.effectSourceId() == ProjectDataManagement::EffectSource::ES_Internal);
if (bDoClaimPlannedCharge)
{
// get full charge amount per charge
QMap<int, int> mapChargeTypeAmount; // QMp<chargeTypeId, chargeAmount>
for (const auto &effector : request.effectors())
mapChargeTypeAmount.insert(effector.chargeStruct().chargeTypeId(), mapChargeTypeAmount.value(effector.chargeStruct().chargeTypeId()) + effector.chargeAmount());
QMapIterator<int, int> it(mapChargeTypeAmount);
while (it.hasNext())
{
it.next();
DEBUG_LOG(m_logger) << "Got new charges" << it.key() << " " << it.value();
int chargeNeeded = it.value();
if (chargeNeeded <= 0)
return Core::FunctionReplyObject(tr_lts("dds", "Invalid impact count (<= 0)."));
if (request.id() == 0)
{
int chargeLeft = m_mainHandler->chargeAdministrationHandler()->availableCharge(it.key(), request.partyId().get());
QString chargeName = QString::fromStdString(m_mainHandler->dds()->pdmDtc()->chargeType(it.key()).name());
QString fireUnitName;
if (request.participantId().is_set())
fireUnitName = QString::fromStdString(m_mainHandler->dds()->ppmDtc()->participant(request.participantId().get()).designator());
if (chargeNeeded > chargeLeft)
return Core::FunctionReplyObject(tr_lts("dds", "Invalid impact count. Fire Unit '%1' has %2 charges (%3), but for this cluster %4 are needed."))
.arg(fireUnitName)
.arg((chargeLeft))
.arg(chargeName)
.arg(chargeNeeded);
}
}
}
Database::AesEffectCluster newData;
QVector<Database::AesEffectorData> newEffectorDataVec;
const QJsonObject propertiesObject = QJsonDocument::fromJson(QByteArray::fromStdString(request.properties())).object();
fillData(request, newData, newEffectorDataVec, m_mainHandler);
DEBUG_LOG(m_logger) << "AUTO newData " << newData.impactCount;
if (!request.properties().empty() && (!request.effectStart().is_set() || request.effectStart().get() == 0))
{
newData.isUsingEffectStateReceived = propertiesObject.value(QStringLiteral("useReceived")).toBool();
}
ObjectEffectCluster *ifs(new ObjectEffectCluster(m_mainHandler, this));
connect(ifs, &ObjectEffectCluster::effectStateChanged, this, &HandlerEffectCluster::onEffectStateChanged);
// Context NASAT: check via chargeTypeId:
const bool bChargeIsAirSupport = gearClass == ProjectDataManagement::GearClass::AirSupport;
ifs->setAirSupportSource(bChargeIsAirSupport);
// Now we always need to send fire ready, never without
newData.effectorState = AesEffectorState::EsNotReadyToFire;
const Core::FunctionReplyObject ret = ifs->createObject(newData, newEffectorDataVec);
if (ret.error())
{
delete ifs;
return ret;
}
DEBUG_LOG(m_logger) << "New EffectCluster";
if (gearClass == ProjectDataManagement::GearClass::AirSupport)
{
if (s_currentAirSupportCount >= s_maxAirSupportCount)
{
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 active air supports."));
}
}
else if (totalAreasCentral() >= (s_maxActiveAreasCentral[AesType::AtEffectCluster]) + (s_currentAirSupportCount) && 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 active central effect cluster."));
}
++m_areaCountCentral;
DEBUG_LOG(m_logger) << "m_areaCountCentral " << m_areaCountCentral;
if (ifs->data().name == propertiesObject[AdlerJson::JsonKeys::FEUERKAMPFREFERENZ()].toString())
{
// Update name
ifs->updateName(QStringLiteral("ZH-Nr. %1").arg(ifs->data().id));
}
m_objectMap.insert(ifs->id(), ifs);
connect(ifs, &ObjectEffectCluster::newAiv, this, &HandlerEffectCluster::newAiv);
connect(ifs, &ObjectEffectCluster::removeAiv, this, &HandlerEffectCluster::removeAiv);
m_referenceToClusterId.insert(propertiesObject[AdlerJson::JsonKeys::FEUERKAMPFREFERENZ()].toString(), ifs->id());
return ret;
}
Core::FunctionReplyObject HandlerEffectCluster::effectClusterModifyRequest(const rti::core::SampleIdentity &cookie, const EffectClusterModifyRequest &request)
{
// // externalId maybe set be HLA interface:
// // from external (externalIdValue>0) -> no partyId required
// // from internal (externalIdValue==0) -> partyId required
// const int externalIdValue = request.externalId().is_set() ? request.externalId().get() : 0;
// if (externalIdValue == 0 && !request.partyId().is_set())
// return Core::FunctionReplyObject(tr_lts("dds", "Explosion areas need a party id."));
DEBUG_LOG(m_logger) << "request.effectStart :" << request.effectStart() << ", effectDuration :"
<< request.effectDuration()
// << ", flightDuration :" << request.flightDuration()
// << ", tapTime :" << request.tapTime()
<< ", properties: " << request.properties();
Core::FunctionReplyObject ret;
if (request.id() == 0 || ((request.id() < 0) && !getObjectFromMap(m_objectMap, std::abs(request.id()), ret)))
return effectClusterCreateRequest(request);
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, std::abs(request.id()), ret);
if (!ifs)
return ret;
ifs->setCookie(cookie);
AreaEffectSimulation::Database::AesEffectCluster aea(ifs->data());
QVector<Database::AesEffectorData> effectorDataVec;
fillData(request, aea, effectorDataVec, m_mainHandler);
const QJsonObject propertiesObject = QJsonDocument::fromJson(QByteArray::fromStdString(aea.properties.toStdString())).object();
// Context NASAT: check via chargeTypeId:
const ProjectDataManagement::GearClass gc = m_mainHandler->gearClassOfChargeTypeId(request.effectors().at(0).chargeStruct().chargeTypeId());
const bool bChargeIsAirSupport = (gc == ProjectDataManagement::GearClass::AirSupport);
ifs->setAirSupportSource(bChargeIsAirSupport);
// For counting, only Elements being at least prepared are important:
if (request.effectStart().is_set() && ifs->isObjectModifiable()) // ToDo: check
{
// Calculate new polygon here. Not nice, though
auto poly = Polygons::effectClusterArea(aea);
std::vector<Core::GeoPosition> _form;
ObjectBase::ddsVecFromPoly(poly, _form);
bool wasCentral = ifs->data().isCentral;
if (bChargeIsAirSupport)
{
if (s_currentAirSupportCount >= s_maxAirSupportCount)
return Core::FunctionReplyObject(1, tr_lts("dds", "Too many active air supports."));
}
else
{
if (totalAreasCentral() >= (s_maxActiveAreasCentral[AesType::AtEffectCluster] + (s_currentAirSupportCount)) && request.effectStart().is_set()
&& ((wasCentral && !ifs->isActivationTimeSet()) || !wasCentral))
return Core::FunctionReplyObject(1, tr_lts("dds", "Too many active central effect clusters."));
}
// For counting, only Elements being at least prepared are important:
}
if (request.id() < 0 && checkStateForSfm(ifs))
{
DEBUG_LOG(m_logger) << "Overwrite Effect State for OC";
aea.effectStateId = AesEffectState::EsPrepared;
}
DEBUG_LOG(m_logger) << "request.properties()" << request.properties() << " aea.properties=" << aea.properties;
// ifs->setEffectors(effectorDataVec);
// updateProperties(aea, effectorDataVec); <- done in fillData
// update internalMap
QString referenceToDelete;
for (const auto &key : m_referenceToClusterId.keys())
{
if (m_referenceToClusterId.value(key) == ifs->id() && key != ifs->name())
referenceToDelete = key;
}
if (!referenceToDelete.isEmpty())
{
m_referenceToClusterId.remove(referenceToDelete);
m_referenceToClusterId.insert(propertiesObject[AdlerJson::JsonKeys::FEUERKAMPFREFERENZ()].toString(), ifs->id());
}
if (aea.impactCount == 0)
{
for (const auto &effector : effectorDataVec)
{
aea.impactCount += effector.chargeAmount;
}
}
return ifs->updateObject(aea, effectorDataVec);
}
void HandlerEffectCluster::recountActiveAreas()
{
s_currentAirSupportCount = 0;
for (const auto *area : qAsConst(m_objectMap))
{
if (area->isObjectInTrainingArea()) // meanwhile an ugly name, means EsPrepared, EsActivated, EsPaused
{
if (m_mainHandler->gearClassOfChargeTypeId(area->data().chargeTypeId) == ProjectDataManagement::GearClass::AirSupport)
++s_currentAirSupportCount;
}
}
HandlerBaseTmpl::recountActiveAreas();
DEBUG_LOG(m_logger) << "m_areaCountCentral after Recount = " << m_areaCountCentral;
DEBUG_LOG(m_logger) << "m_areaCount s_currentAirSupportCount after Recount = " << s_currentAirSupportCount;
}
void HandlerEffectCluster::onEffectClusterRemoveRequest(const rti::core::SampleIdentity &cookie, const Core::RemoveElementRequest &request)
{
DEBUG_LOG(m_logger) << "************************************ onEffectClusterRemoveRequest: " << request;
m_mainHandler->dds()->fts()->effectClusterRemoveReply(cookie, removeReply(&DatabaseInterface::removeAesEffectCluster, request));
}
void HandlerEffectCluster::onEffectClusterPauseResumeRequest(const rti::core::SampleIdentity &cookie, const EffectClusterPauseResumeRequest &request)
{
DEBUG_LOG(m_logger) << "onEffectClusterPauseResumeRequest" << request;
Core::FunctionReplyObject ret;
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, request.id(), ret);
if (!ifs)
{
m_mainHandler->dds()->fts()->effectClusterPauseResumeReply(cookie, ret);
return;
}
ret = ifs->resumePause(request);
if (ret.error())
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->effectClusterPauseResumeReply(cookie, ret);
}
void HandlerEffectCluster::onEffectClusterFireHaltRequest(const rti::core::SampleIdentity &cookie, const EffectClusterFireHaltRequest &request)
{
DEBUG_LOG(m_logger) << "onEffectClusterFireHaltRequest" << request;
Core::FunctionReplyObject ret;
ProjectDataManagement::L22_FireHaltCategory categorie = request.fireHaltCategory();
QMap<int, ObjectEffectCluster *> effectClusterPartyMap;
for (const auto *area : qAsConst(m_objectMap))
{
if (area->data().partyId == request.partyId())
{
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, area->data().id, ret);
if (!ifs)
{
m_mainHandler->dds()->fts()->effectClusterFireHaltReply(cookie, ret);
return;
}
effectClusterPartyMap.insert(area->data().id, ifs);
}
}
switch (categorie.underlying())
{
case ProjectDataManagement::L22_FireHaltCategory::A: // Feuer Halt Allgemein
{
// all control operations are halted
for (auto *ifs : qAsConst(effectClusterPartyMap))
{
EffectClusterPauseResumeRequest req;
req.id(ifs->data().id);
req.remainingDuration(ifs->data().effectDuration);
req.resumeExecution(false); // false -> pause aes
ret = ifs->resumePause(req);
}
break;
}
case ProjectDataManagement::L22_FireHaltCategory::R: // Feuer Halt Referenziert
{
// Ongoing combat operations are halted after comparing the extent of
// the fire hold with the corresponding target coordinate and circular area.
for (auto *ifs : qAsConst(effectClusterPartyMap))
{
const double lonRad = request.longitude().is_set() ? request.longitude().get() * LtsCoordTrans::deg2rad() : 0.0;
const double latRad = request.latitude().is_set() ? request.latitude().get() * LtsCoordTrans::deg2rad() : 0.0;
const int radius = request.radius().is_set() ? request.radius().get() : 0;
DEBUG_LOG(m_logger) << "[Feuer Halt Referenziert] " << "lonRad : " << lonRad << " " << "latRad : " << latRad << " " << "radius : " << radius;
if (ifs->intersectWithCircle(lonRad, latRad, radius))
{
DEBUG_LOG(m_logger) << "[Feuer Halt Referenziert ----- intersectWithCircle] " << "effectClusterId: " << ifs->data().id;
EffectClusterPauseResumeRequest req;
req.id(ifs->data().id);
req.remainingDuration(ifs->data().effectDuration);
req.resumeExecution(false); // false -> pause aes
ifs->resumePause(req);
}
}
break;
}
case ProjectDataManagement::L22_FireHaltCategory::E: // Feuer Einstellen
{
// Cease Fire (Category E) messages are issued when continuing an engagement is no longer viable.
// Unlike "Check Fire" (Feuer Halt), any rounds already loaded in a Panzerhaubitze (PzH) are still fired,
// followed by an EX-FQ(FN) report. Once the weapon system confirms execution,
// the engagement is closed and the target area status is set to "expired."
for (auto *ifs : qAsConst(effectClusterPartyMap))
{
DEBUG_LOG(m_logger) << "[Feuer Einstellen ----- markAsExpired] " << "effectClusterId: " << ifs->data().id;
ifs->markAsExpired(request.entladenSchuss().is_set() ? request.entladenSchuss().get() : false);
}
break;
}
case ProjectDataManagement::L22_FireHaltCategory::C: // Aufheben Feuer Halt Allgemein
for (auto *ifs : qAsConst(effectClusterPartyMap))
{
DEBUG_LOG(m_logger) << "[Aufheben Feuer Halt Allgemein] " << "effectClusterId: " << ifs->data().id;
EffectClusterPauseResumeRequest req;
req.remainingDuration(ifs->data().effectDuration);
req.resumeExecution(true); // true -> resume aes
ifs->resumePause(req);
}
break;
case ProjectDataManagement::L22_FireHaltCategory::F: // Aufheben Feuer Halt Referenziert
{
for (auto *ifs : qAsConst(effectClusterPartyMap))
{
const double lonRad = request.longitude().is_set() ? request.longitude().get() * LtsCoordTrans::deg2rad() : 0.0; // optional<lonRad>
const double latRad = request.latitude().is_set() ? request.latitude().get() * LtsCoordTrans::deg2rad() : 0.0; // optional <latRad>
const int radius = request.radius().is_set() ? request.radius().get() : 0;
DEBUG_LOG(m_logger) << "[Aufheben Feuer Halt Referenziert] " << "lonRad : " << lonRad << " " << "latRad : " << latRad << " " << "radius : " << radius;
if (ifs->intersectWithCircle(lonRad, latRad, radius))
{
DEBUG_LOG(m_logger) << "[Aufheben Feuer Halt Referenziert ----- intersectWithCircle] " << "effectClusterId: " << ifs->data().id;
EffectClusterPauseResumeRequest req;
req.id(ifs->data().id);
req.remainingDuration(ifs->data().effectDuration);
req.resumeExecution(true); // true -> resume aes
ifs->resumePause(req);
}
}
break;
}
default:
INFO_LOG(m_logger) << "EX-FH missing or wrong FireHaltCategory" << categorie.underlying();
break;
}
if (ret.error())
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->effectClusterFireHaltReply(cookie, ret);
}
void HandlerEffectCluster::onEffectClusterReleaseReceivedRequest(const rti::core::SampleIdentity &cookie, const EffectClusterReleaseReceivedRequest &request)
{
DEBUG_LOG(m_logger) << "onEffectClusterReleaseReceivedRequest" << request;
Core::FunctionReplyObject ret(request.id());
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, request.id(), ret);
if (!ifs)
{
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->effectClusterReleaseReceivedReply(cookie, ret);
return;
}
if (!ifs->isUsingEffectStateReceived())
{
ret = ret.setStr(tr_lts("dds", "EffectCluster with id %1 is not using EffectState Received")).arg(request.id());
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->effectClusterReleaseReceivedReply(cookie, ret);
return;
}
ifs->setUseEffectStateReceived(false);
ifs->checkEnterNewState();
if (ret.error())
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->effectClusterReleaseReceivedReply(cookie, ret);
}
void HandlerEffectCluster::onAcknowledgeExternalAesItemRequest(const rti::core::SampleIdentity &cookie, const AcknowledgeExternalAesItemRequest &request)
{
DEBUG_LOG(m_logger) << "HandlerEffectCluster::onAcknowledgeExternalAesItemRequest for aesItem" << request.aesId();
Core::FunctionReplyObject ret(request.aesId());
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, request.aesId(), ret);
if (!ifs)
{
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->acknowledgeExternalAesItemReply(cookie, ret);
return;
}
ifs->setUseEffectStateReceived(false);
ifs->checkEnterNewState();
if (ret.error())
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->acknowledgeExternalAesItemReply(cookie, ret);
}
void HandlerEffectCluster::onSetAesItemEffectorStateRequest(const rti::core::SampleIdentity &cookie, const SetAesItemEffectorStateRequest &request)
{
DEBUG_LOG(m_logger) << "HandlerEffectCluster::onSetAesItemEffectorStateRequest for aesItem" << request.aesId() << " to State: " << request.newEffectorState();
Core::FunctionReplyObject ret(request.aesId());
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, request.aesId(), ret);
if (!ifs)
{
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->setAesItemEffectorStateReply(cookie, ret);
return;
}
if (!ifs->overrideEffectorStates(request.newEffectorState()))
{
ret.setStr(tr_lts("dds", "EffectCluster with id %1 can't change to new State")).arg(request.aesId());
}
m_mainHandler->dds()->fts()->setAesItemEffectorStateReply(cookie, ret);
}
void HandlerEffectCluster::onAdlerFqFbReply(const Core::FunctionReplyObject &replyObject)
{
DEBUG_LOG(m_logger) << "HandlerEffectCluster::onAdlerFqFbReply " << replyObject.id();
QJsonObject property;
property[QStringLiteral("readyToFire")] = true;
m_objectMap[replyObject.id()]->setAesProperty(property);
}
/*
void HandlerEffectCluster::onTargetPositionsModifyRequest(const rti::core::SampleIdentity &cookie, const TargetPositionsModifyRequest &request)
{
DEBUG_LOG(m_logger) << "onTargetPositionsModifyRequest" << request;
Core::FunctionReplyObject ret;
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, request.aesItemId(), ret);
if (!ifs)
{
m_mainHandler->dds()->fts()->targetPositionsModifyReply(cookie, ret);
return;
}
ret = ifs->targetPositionsModify(request);
if (ret.error())
ERROR_LOG(m_logger) << ret();
m_mainHandler->dds()->fts()->targetPositionsModifyReply(cookie, ret);
}
*/
void HandlerEffectCluster::onTrajectoryAdlerModifyRequest(const rti::core::SampleIdentity &cookie, const TrajectoryAdlerModifyRequest &request)
{
DEBUG_LOG(m_logger) << "onTrajectoryAdlerModifyRequest" << request;
Core::FunctionReplyObject ret;
ObjectEffectCluster *ifs = getObjectFromMap(m_objectMap, request.aesItemId(), ret);
if (!ifs)
{
m_mainHandler->dds()->fts()->trajectoryAdlerModifyReply(cookie, ret);
return;
}
ret = ifs->trajectoryAdlerModify(request);
DEBUG_LOG(m_logger) << "onTrajectoryAdlerModifyRequest 1";
if (ret.error())
ERROR_LOG(m_logger) << ret();
DEBUG_LOG(m_logger) << "onTrajectoryAdlerModifyRequest 2";
m_mainHandler->dds()->fts()->trajectoryAdlerModifyReply(cookie, ret);
DEBUG_LOG(m_logger) << "onTrajectoryAdlerModifyRequest 3";
}
} // Namespace AreaEffectSimulation