/** * Base Class for Effector Firing Strategy * $Id$ * $HeadURL$ * $Author$ * Created by Hannes Fritsche, ESZS, 2025-05-11 */ #include "abstract_firing_strategy.h" #include "object_effector.h" #include "main_handler.h" #include "handler_charge_effect.h" #include #include "Trajectory/trajectory_transform.h" #include #include #include #include namespace AreaEffectSimulation { AbstractFiringStrategy::AbstractFiringStrategy(ObjectEffector *parent) : QObject(parent) , m_effector(parent) , m_logger(getLogger("FiringStrategy")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } AbstractFiringStrategy::~AbstractFiringStrategy() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool AbstractFiringStrategy::calculateDispersionTrajectory() { DEBUG_LOG(m_logger) << "calculateDispersionTrajectory"; // 1. Neuen Zielpunkt berechnen const auto intendendTrajectory = m_effector->intendendTrajectory(); // sanity check: if (intendendTrajectory.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " size of intendendTrajectory not sufficiant, size=" << intendendTrajectory.size(); return false; } LtsUtils::QPoint3D newTarget = intendendTrajectory.last().position; const int scatterLength = qRound(m_effector->effectorData().mhpScatterLenght()); const int randomValueX = QRandomGenerator::global()->bounded(-scatterLength, scatterLength + 1); const int scatterWidth = qRound(m_effector->effectorData().mhpScatterWidth()); const int randomValueY = QRandomGenerator::global()->bounded(-scatterWidth, scatterWidth + 1); newTarget.setX(newTarget.x() + randomValueX); newTarget.setY(newTarget.y() + randomValueY); // 2. Neue Trajektorie berechnen m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(intendendTrajectory, newTarget); // sanity check: if (m_transformedTrajectory.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " size of m_transformedTrajectory not sufficiant, size=" << m_transformedTrajectory.size(); return false; } return true; } std::vector AbstractFiringStrategy::buildTrajectoryGeometry(const QVector &relativeTrajectory) { // sanity check: if (relativeTrajectory.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " size of relativeTrajectory not sufficiant, size=" << relativeTrajectory.size(); return {}; } // setup some vars of effector, which is used as launch position: Core::GeoPosition effectorPositionGeo = participantPosition(m_effector->participantId()); const LtsCoordTrans::UtmCoordinate &launchUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(effectorPositionGeo.lon(), effectorPositionGeo.lat()); std::vector geometry; geometry.reserve(relativeTrajectory.size()); // Aehnlich wie Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/aes_handler.cpp Zeile 365 for (const TrajectoryPoint &trajPntRelPos : qAsConst(relativeTrajectory)) { DEBUG_LOG(m_logger) << "Flugbahnpunkt: trajPntRelPos :" << trajPntRelPos; // benefit from operator<<: // // geographic offers nice feature to add an offset: // const QPointF &trajectoryAbsGeo = launchUtm.geographic(trajPntRelPos); // but lets stay with trajectoryAbsUtm to enable DEBUG_LOG const QPointF &trajPntRelPosUtm = trajPntRelPos.position.toPointF(); // still relativ to launch const LtsCoordTrans::UtmCoordinate trajPntAbsUtm(launchUtm.zone, launchUtm.x + trajPntRelPosUtm.x(), launchUtm.y + trajPntRelPosUtm.y()); const QPointF &trajPntAbsGeo = trajPntAbsUtm.geographic(); // Position des Participants mit der m_participantId const double zValue = effectorPositionGeo.z() + trajPntRelPos.position.z(); // trajPntRelPos.position.z() ist die Hoehe DEBUG_LOG(m_logger) << "trajPntAbsUtm :" << trajPntAbsUtm << ", trajPntAbsGeo :" << trajPntAbsGeo; geometry.emplace_back(Core::GeoPositionRelativeTime(trajPntAbsGeo.x(), trajPntAbsGeo.y(), zValue, trajPntRelPos.time)); } return geometry; } TrajectoryTimeAware AbstractFiringStrategy::buildTrajectoryTimeAware(const std::vector &trajectoryGeometry, qint64 trajectoryStartTime, int index) { // sanity check: if (trajectoryGeometry.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " size of trajectoryGeometry not sufficiant, size=" << trajectoryGeometry.size(); return {}; } TrajectoryTimeAware trajectory; trajectory.trajectoryStartTime(trajectoryStartTime); trajectory.index(index); // see SituationMap : charge_effect_to_map ::updateSingleTrajectory() // [E01562-259] use originatorId, originatorTypeId (instead of aesItemId, aesTypeId) trajectory.originatorId(m_effector->aesItemId()); trajectory.originatorTypeId(ProjectDataManagement::StateChangeOriginator::ChargeEffect); trajectory.geometry(trajectoryGeometry); return trajectory; } int AbstractFiringStrategy::sendChargeEffect(const TrajectoryTimeAware &trajectoryTimeAware, const QJsonObject &properties) { DEBUG_LOG(m_logger) << "sendChargeEffect"; const auto trajGeometry = trajectoryTimeAware.geometry(); // sanity check: if (trajGeometry.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " size of trajectoryTimeAware.geometry not sufficiant, size=" << trajGeometry.size(); return -1; } const qint64 trajectoryStartTime = trajectoryTimeAware.trajectoryStartTime(); ChargeEffectModifyRequest chargeEffect; chargeEffect.id(0); chargeEffect.effectDuration(3000); chargeEffect.name("Schuss"); chargeEffect.chargeTypeId(m_effector->effectorData().chargeStruct().chargeTypeId()); chargeEffect.effectStart(trajectoryStartTime + trajectoryTimeAware.geometry().back().relativeTime()); // Einschlag Zeit chargeEffect.igniterTypeId(m_effector->effectorData().chargeStruct().igniterTypeId()); chargeEffect.gearTypeId(gearTypeId(m_effector->effectorData().chargeStruct().chargeTypeId(), m_effector->participantId())); chargeEffect.originatorId(m_effector->aesItemId()); chargeEffect.originatorTypeId(ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation); chargeEffect.properties(QJsonDocument(properties).toJson().toStdString()); const Core::GeoPosition &impactPosition = trajGeometry.back(); chargeEffect.position(impactPosition); // Impact Position chargeEffect.trajectoryTimeAware(trajectoryTimeAware); const auto &reply = m_effector->mainHandler()->chargeEffectHandler()->createChargeEffect(chargeEffect); DEBUG_LOG(m_logger) << LOGFUNCTION << " reply.id=" << reply.id() << ", str:" << reply.str() << ", uniqueId:" << reply.uniqueId(); return reply.id(); } Core::GeoPosition AbstractFiringStrategy::participantPosition(int participantId) { m_effector->mainHandler()->dds()->ppmDtc()->enableParticipantPosition(true); const ParticipantManagement::ParticipantPosition &pos = m_effector->mainHandler()->dds()->ppmDtc()->participantPosition(participantId); return pos.pos(); } int32_t AbstractFiringStrategy::gearTypeId(int chargeTypeId, int participantId) { DEBUG_LOG(m_logger) << "getGearTypeId" << ", chargeTypeId:" << chargeTypeId << ", participantId:" << participantId; m_effector->mainHandler()->dds()->ppmDtc()->enableDynamicParticipantData(true); m_effector->mainHandler()->dds()->ppmDtc()->waitForHistoricalDataDynamicParticipantData(1000); const ParticipantManagement::DynamicParticipantData &dynamicParticipantData = m_effector->mainHandler()->dds()->ppmDtc()->dynamicParticipantData(participantId); DEBUG_LOG(m_logger) << "gearSize " << dynamicParticipantData.gear().size(); const auto& participantData_gear = dynamicParticipantData.gear(); for (const auto &gear : qAsConst(participantData_gear)) { DEBUG_LOG(m_logger) << "gear.chargeTypeId(): " << gear.chargeTypeId() << ", chargeTypeId:" << chargeTypeId; if (gear.chargeTypeId() == chargeTypeId) { DEBUG_LOG(m_logger) << "getGearTypeId return: " << gear.gearTypeId(); if (gear.gearTypeId() != 0) return gear.gearTypeId(); else return 701; // MALTE XKCD } } return 701; // MALTE XKCD } qint64 AbstractFiringStrategy::getStartTime() const { const auto trajectoryAndEffectStartTime = m_effector->mainHandler()->dds()->currentPartitionTimeMSec() + 1000; return trajectoryAndEffectStartTime; } } // namespace AreaEffectSimulation