/** * Handle an impact from nasat aircraft and create single explosion * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2018-08-30 */ #include "handler_nasat_impact.h" #include "initialization.h" #include "dds_handler.h" #include "handler_participant.h" #include #include #include #include #include // #define TRIGGER_DUMMY_IMPACT #ifdef TRIGGER_DUMMY_IMPACT # include #endif // TRIGGER_DUMMY_IMPACT namespace GuezNasat { HandlerNasatImpact::HandlerNasatImpact(Initialization *mainHandler) : AreaEffectSimulation::Interface::Ftc::AirSupportImpactModifyReplyHandler() , m_mainHandler(mainHandler) , m_logger(getLogger("HandlerNasatImpact")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; auto pdmDtc = m_mainHandler->dds()->pdmDtc(); connect(pdmDtc.get(), &ProjectDataManagement::Interface::Dtc::chargeTypeChanged, this, &HandlerNasatImpact::onChargeTypeChanged); pdmDtc->chargeTypes([this](const ProjectDataManagement::ChargeType &ct) { onChargeTypeChanged(ct);}); #ifdef TRIGGER_DUMMY_IMPACT FATAL_LOG(m_logger) << LOGFUNCTION << " prepare singleShot..."; QTimer *timer = new QTimer; timer->setInterval(30000); connect(timer, &QTimer::timeout, [this] () { FATAL_LOG(m_logger) << LOGFUNCTION << " singleShot..."; NasatWeaponRelease wr; wr.aircraftId = 1; GeoPosition2D geoPos; geoPos.lon = 11.56; // degree geoPos.lat = 52.46; // degree wr.impactPosition = geoPos; wr.flightTime = 5; wr.munition = Munition::MSL_AGM_114K; // MSL_AGM_114K : 29 -> 499 // wr.munition = Munition::Missile_MK_24; // Missile_MK_24: 41 -> 511 wr.fuseType = FuseType::Instant; wr.releaseMode = ReleaseMode::Undefined; this->nasatWeaponRelease(wr); FATAL_LOG(m_logger) << LOGFUNCTION << " singleShot...DONE"; }); timer->start(); #endif // TRIGGER_DUMMY_IMPACT TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void HandlerNasatImpact::onAirSupportImpactModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) WARNING_LOG(m_logger) << "Could not create single explosion for nasat impact: " << reply(); #ifdef NLDEVCTRL_844 testing.replyCounter++; INFO_LOG(m_logger) << "Got create reply: " << reply() << "(id=" << reply.id() << ")" << ", this=" << this << " - replyCounter=" << testing.replyCounter; #else INFO_LOG(m_logger) << "Got create reply: " << reply() << "(id=" << reply.id() << ")" << ", this=" << this; #endif // NLDEVCTRL_844 } void HandlerNasatImpact::onExplosionClusterModifyReply(const Core::FunctionReplyObject &reply) { INFO_LOG(m_logger) << "Got create reply: " << reply() << "(id=" << reply.id() << ")" << ", this=" << this << ", reply.error()=" << reply.error(); if (reply.error()) ERROR_LOG(m_logger) << "onExplosionClusterModifyReply: Got reply: " << reply() << "(id=" << reply.id() << ")" << " '" << reply.translated() << "'"; } void HandlerNasatImpact::nasatWeaponRelease(const GuezNasat::NasatWeaponRelease &weaponRelease) { const Core::GeoPosition geoPos(weaponRelease.impactPosition.lon * LtsCoordTrans::deg2rad(), weaponRelease.impactPosition.lat * LtsCoordTrans::deg2rad(), 0); // z == 0? Ground... TODO double tsWeaponRelease = weaponRelease.timestamp < 0 ? QDateTime::currentSecsSinceEpoch() : weaponRelease.timestamp; // AES needs time to execute a) calculations of trajectory and b) intersections ('preStartTimer') // add 5sec, an additional period of time: tsWeaponRelease += 5; qint64 impactTimeMsec = qRound64((tsWeaponRelease + weaponRelease.flightTime) * 1000); qint64 weaponReleaseMsec = qRound64(weaponRelease.flightTime * 1000); nasatImpact(weaponRelease.aircraftId, geoPos, weaponReleaseMsec, impactTimeMsec, static_cast(weaponRelease.munition), weaponRelease.fuseType); } void HandlerNasatImpact::nasatImpact(int nasatId, const Core::GeoPosition &pos, qint64 flightTimeMsec, qint64 impactTimeMsec, int munitionCode, FuseType fuseType) { #ifdef NLDEVCTRL_844 static bool bEnable = true; if (!bEnable) return; #endif // NLDEVCTRL_844 DEBUG_LOG(m_logger) << LOGFUNCTION << " START, nasatId=" << nasatId << ", pos=" << pos << ", flightTimeMsec = " << flightTimeMsec << ", impactTimeMsec = " << impactTimeMsec; const auto participants = m_mainHandler->handlerParticipant()->participantsForNasatId(nasatId); if (participants.isEmpty()) { WARNING_LOG(m_logger) << "Can not find a valid participant for nasat aircraft with id " << nasatId; return; } const int chargeTypeIdVal = chargeTypeFromMunitionCode(munitionCode); if (chargeTypeIdVal < 0) return; const QDateTime dtNow = QDateTime::currentDateTime(); const qint64 tNow = dtNow.toMSecsSinceEpoch() + 5000; // 5s for Tetra - really needed? AreaEffectSimulation::ExplosionClusterSingleEffectOnlyRequest ecReq; ecReq.id(0); ecReq.effectStart(impactTimeMsec > tNow ? impactTimeMsec : tNow); // msec ecReq.name(QStringLiteral("NASAT_%1_%2_%3").arg(nasatId).arg(munitionCode).arg(dtNow.toString(QStringLiteral("dd hh:mm:ss"))).toStdString()); ecReq.element(ecReq.name()); ecReq.impactPosition(Core::GeoPosition2D(pos.lon(), pos.lat())); ecReq.chargeTypeId(chargeTypeIdVal); // ecReq.externalId(815); // ecReq.flightTime(flightTime); if (m_logger.isDebugEnabled()) { const Core::GeoPosition &nasatPos = m_mainHandler->handlerParticipant()->nasatPosition(nasatId); DEBUG_LOG(m_logger) << LOGFUNCTION << " START, nasatId=" << nasatId << ", pos=" << nasatPos << ", flightTimeMsec = " << flightTimeMsec << ", impactTimeMsec=" << impactTimeMsec; } AreaEffectSimulation::ExplosionClusterModifyRequest ecCommonModifyRequest; ecCommonModifyRequest.id(0); ecCommonModifyRequest.effectStart(impactTimeMsec > tNow ? impactTimeMsec : tNow); // msec ecCommonModifyRequest.effectDuration(1000); ecCommonModifyRequest.effectSourceId(ProjectDataManagement::EffectSource::ES_Internal); // charge and geometry: ecCommonModifyRequest.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint); // ecCommonModifyRequest.zog(0); ecCommonModifyRequest.chargeStruct().chargeTypeId(chargeTypeIdVal); ecCommonModifyRequest.chargeStruct().igniterTypeId(fuseTypeToPdmIgniterType(fuseType)); ecCommonModifyRequest.area().cornerBottomLeft(Core::GeoPosition2D(pos.lon(), pos.lat())); ecCommonModifyRequest.name(QStringLiteral("NASAT_%1_%2_%3").arg(nasatId).arg(munitionCode).arg(dtNow.toString(QStringLiteral("dd hh:mm:ss"))).toStdString()); ecCommonModifyRequest.element(ecReq.name()); ecCommonModifyRequest.mhpCount(1); ecCommonModifyRequest.impactCount(1); // ecCommonModifyRequest.partyId(request.partyId()); // ecCommonModifyRequest.participantId(-1); // -1 -> avoid trajectory ecCommonModifyRequest.flightDuration(flightTimeMsec); // request.flightDuration in msec // AreaEffectSimulation::AirSupportImpactModifyRequest asi; // asi.name(QStringLiteral("NASAT_a%1_m%2_t%3").arg(nasatId).arg(munitionCode).arg(dtNow.toString(QStringLiteral("dd hh:mm:ss"))).toStdString()); // asi.effectStart(impactTimeMsec > tNow ? impactTimeMsec : tNow); // asi.chargeTypeId(chargeTypeFromMunitionCode(munitionCode)); // if (asi.chargeTypeId() < 0) // return; // asi.impactPosition(Core::GeoPosition2D(pos.lon(), pos.lat())); // asi.flightTime(flightTime); DEBUG_LOG(m_logger) << " Sending air support impact: number of participant= " << participants.size(); for (const NasatAircraftParticipantEx *nap : participants) { // asi.partyId(nap->partyId()); // asi.participantId(nap->participantId()); ecReq.partyId(nap->partyId()); ecCommonModifyRequest.partyId(nap->partyId()); ecCommonModifyRequest.participantId(nap->participantId()); // ecCommonModifyRequest.externalId(nap->participantId()); // externalId -> would be like like HLA and means no trajectory int success = nap->publisher()->publish(ecCommonModifyRequest, this); if (success) DEBUG_LOG(m_logger) << "Sending air support impact create request: " << ecReq << " to exercise " << nap->exerciseId() << ", nap=" << nap->participant() << " -> success = " << success; else ERROR_LOG(m_logger) << "Error on Sending air support impact create request: " << ecReq << " to exercise " << nap->exerciseId() << ", nap=" << nap->participant() << " -> success = " << success; #ifdef NLDEVCTRL_844 testing.requestCounter++; INFO_LOG(m_logger) << " requestCounter = " << testing.requestCounter; #endif // NLDEVCTRL_844 } #ifdef NLDEVCTRL_844 bool bOk = (testing.requestCounter - testing.replyCounter) < 23; FATAL_LOG(m_logger) << " bOk= " << bOk << ", requestCounter = " << testing.requestCounter << ", replyCounter = " << testing.replyCounter; if (!bOk) { FATAL_LOG(m_logger) << " bOk= " << bOk << ", requestCounter = " << testing.requestCounter << " -> DISABLE"; bEnable = false; abort(); } if (testing.requestCounter > 70) { FATAL_LOG(m_logger) << " bOk= " << bOk << ", requestCounter = " << testing.requestCounter << " -> RESTART"; exit(42); // trigger restart } #endif // NLDEVCTRL_844 } void HandlerNasatImpact::onChargeTypeChanged(const ProjectDataManagement::ChargeType &ct) { const QJsonDocument doc(QJsonDocument::fromJson(QByteArray::fromRawData(ct.properties().c_str(), ct.properties().size()))); const QJsonObject obj(doc.object()); if (!obj.contains(QLatin1String("nasatId"))) return; int nasatId = obj.value(QLatin1String("nasatId")).toInt(); if (nasatId == 0) { WARNING_LOG(m_logger) << "Found charge with an invalid property 'nasatId', id: " << ct.id(); return; } m_chargeMapping.insert(nasatId, ct.id()); } int HandlerNasatImpact::chargeTypeFromMunitionCode(int nasatChargeCode) const { auto it = m_chargeMapping.find(nasatChargeCode); if (it != m_chargeMapping.end()) return it.value(); WARNING_LOG(m_logger) << "Got unmapped nasat charge code " << nasatChargeCode; Q_ASSERT(false); return -1; } int HandlerNasatImpact::fuseTypeToPdmIgniterType(FuseType fusetype) const { // return = igniterTypeId from table pdm_igniter_type switch (fusetype) { case FuseType::Instant: return 102; // Impact Default case FuseType::Delay: return 103; // Delay Default case GuezNasat::FuseType::AirBurst: return 101; // Proxmimity Default default: return 102; } } } // namespace GuezNasat