923 lines
38 KiB
C++
Executable File
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
|