Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Services/AreaEffectSimulation/handler_effect_cluster.cpp
T
berrux-dev 472f537313 Migrates AreaEffectSimulation to generated rtiddsgen API
Converts accessor calls to plain member access across aes service and plugin.
2026-09-21 02:45:07 +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 <QtGui/QPolygonF>
#include <ProjectDataManagement/pdm_translation.h>
#include <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[static_cast<int>(AesType::AtEffectCluster)],
s_areaCountCentral[static_cast<int>(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::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[static_cast<int>(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::EgAimPoint)
|| (src.explosionGeometry == AreaEffectSimulation::AesExplosionGeometry::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::EgPoint;
else
ecCommonModifyRequest.explosionGeometry = AreaEffectSimulation::AesExplosionGeometry::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::GearClass_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::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::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[static_cast<int>(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::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[static_cast<int>(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::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)
{
case ProjectDataManagement::L22_FireHaltCategory::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::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::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::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::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;
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