/** * Handler for AES data received from SFM * $Id$ * $HeadURL$ * $Author$ * Created by Philipp Dassow, NLST, 2018-04-11 */ #include #include #include #include "handler_aes_data.h" #include "settings.h" namespace TrainingAreaBaseOC { // mathematical positive north -> mathematical negativ, east = 0°, 0,01 degree in 1/100° -> factor 100 static inline int compassToMathAngleForSfm(double compassDegree) { int ret = qRound((90.0 - compassDegree / 100.0)); while (ret < 0) ret += 360; while (ret > 360) ret -= 360; return ret; } HandlerAesData::HandlerAesData(const std::shared_ptr &dds, const std::shared_ptr &aesDtc, const std::shared_ptr &aesFtc, const std::shared_ptr &pdmDtc) : m_dds(dds) , m_aesDtc(aesDtc) , m_aesFtc(aesFtc) , m_pdmDtc(pdmDtc) , m_utmCoord(LtsCoordTrans::UtmCoordinate::fromMgrs(Settings::baseCoord())) , m_logger(getLogger("HandlerAesData")) { } bool HandlerAesData::init() { // Enable AES DTC if (!m_aesDtc->enableBCContamination() || !m_aesDtc->enableExplosionArea() || !m_aesDtc->enableExplosionSingle() || !m_aesDtc->enableFunctionalArea() || !m_aesDtc->enableNContamination() || !m_aesDtc->enableMineArea() || !m_aesDtc->enableExplosionCluster()) { ERROR_LOG(m_logger) << "Error enabling AES Data-Topics."; return false; } // Enable AES FTC if (!m_aesFtc->enableBCContaminationModify() || !m_aesFtc->enableExplosionAreaModify() || !m_aesFtc->enableExplosionSingleModify() || !m_aesFtc->enableFunctionalAreaModify() || !m_aesFtc->enableNContaminationModify() || !m_aesFtc->enableExplosionClusterOcModify() || !m_aesFtc->enableMineAreaModifyFromCtaData() || !m_aesFtc->enableBCContaminationRemove() || !m_aesFtc->enableExplosionAreaRemove() || !m_aesFtc->enableExplosionSingleRemove() || !m_aesFtc->enableFunctionalAreaRemove() || !m_aesFtc->enableNContaminationRemove() || !m_aesFtc->enableMineAreaRemove() || !m_aesFtc->enableExplosionClusterRemove() || !m_aesFtc->enableExplosionAreaPauseResume() || !m_aesFtc->enableMineAreaReferenceModify()) { ERROR_LOG(m_logger) << "Error enabling AES Function-Topics."; return false; } // Enable PDM DTC if (!m_pdmDtc->enableChargeType() || !m_pdmDtc->enableGearTypeChargeType() || !m_pdmDtc->enableGearType()) { ERROR_LOG(m_logger) << "Error enabling PDM Data-Topic."; return false; } if (!m_aesDtc->waitForHistoricalDataBCContamination(10000) || !m_aesDtc->waitForHistoricalDataExplosionArea(10000) || !m_aesDtc->waitForHistoricalDataExplosionSingle(10000) || !m_aesDtc->waitForHistoricalDataFunctionalArea(10000) || !m_aesDtc->waitForHistoricalDataNContamination(10000) || !m_aesDtc->waitForHistoricalDataMineArea(10000) || !m_aesDtc->waitForHistoricalDataExplosionCluster(10000)) { ERROR_LOG(m_logger) << "Error waiting for historical data AES."; return false; } DEBUG_LOG(m_logger) << "Initialisation successful"; return true; } void HandlerAesData::process(const QByteArray &data, int multiplexer) { if (data.isEmpty()) return; switch (multiplexer) { case MineArea: { mineAreas(data); break; } case ExplosionArea: { explosionAreas(data); break; } case ExplosionSingle: { explosionSingles(data); break; } case NContamination: { nContaminations(data); break; } case BcContamination: { bcContaminations(data); break; } case FunctionalArea: { functionalAreas(data); break; } case ExplosionAreaStop: { explosionAreasPause(data); break; } case ExplosionAreaContinue: { explosionAreasResume(data); break; } default: WARNING_LOG(m_logger) << "Got unknown multiplexer."; } } void HandlerAesData::specificsExercise(const OcTelegrams::SpecificsExercise &data) { m_utmCoord = LtsCoordTrans::UtmCoordinate::fromMgrs(QString::number(data.zone) + QLatin1Char(data.band) + QString::fromLatin1(data.square) + QString::number(data.northing).rightJustified(7, QLatin1Char('0')) + QString::number(data.easting).rightJustified(7, QLatin1Char('0'))); } void HandlerAesData::onMineAreaModifyFromCtaDataReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Mine area modify failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Mine area modify successfull. Id: " << reply.id(); } void HandlerAesData::onNContaminationModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "N contamination modify failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "N contamination modify successfull. Id: " << reply.id(); } void HandlerAesData::onFunctionalAreaModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Functional area modify failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Functional area modify successfull. Id: " << reply.id(); } void HandlerAesData::onExplosionSingleModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Explosion single modify failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Explosion single modify successfull. Id: " << reply.id(); } void HandlerAesData::onExplosionAreaModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Explosion area modify failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Explosion area modify successfull. Id: " << reply.id(); } void HandlerAesData::onBCContaminationModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "BC contamination modify failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "BC contamination modify successfull. Id: " << reply.id(); } void HandlerAesData::onExplosionClusterOcModifyReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "ExplosionCluster modify failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "ExplosionCluster successfull. Id: " << reply.id(); } void HandlerAesData::onMineAreaRemoveReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Mine area remove failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Mine area remove successfull. Id: " << reply.id(); } void HandlerAesData::onNContaminationRemoveReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "N contamination remove failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "N contamination remove successfull. Id: " << reply.id(); } void HandlerAesData::onFunctionalAreaRemoveReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Functional area remove failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Functional area remove successfull. Id: " << reply.id(); } void HandlerAesData::onExplosionSingleRemoveReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Explosion single remove failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Explosion single remove successfull. Id: " << reply.id(); } void HandlerAesData::onExplosionAreaRemoveReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Explosion area remove failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Explosion area remove successfull. Id: " << reply.id(); } void HandlerAesData::onBCContaminationRemoveReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "BC contamination remove failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "BC contamination remove successfull. Id: " << reply.id(); } void HandlerAesData::onExplosionClusterRemoveReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "ExplosionCluster remove failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "ExplosionCluster remove successfull. Id: " << reply.id(); } void HandlerAesData::onExplosionAreaPauseResumeReply(const Core::FunctionReplyObject &reply) { if (reply.error()) ERROR_LOG(m_logger) << "Explosion area pause/resume failed: " << reply.translated(); else DEBUG_LOG(m_logger) << "Explosion area pause/resume successfull. Id: " << reply.id(); } template static inline void getBasicData(const QByteArray &data, size_t &pos, T &req, bool withoutEffectStart = false) { uint64_t tmp; LtsUtils::get2Byte(data, pos, tmp); req.id(tmp); req.name(data.mid(pos, 8).toStdString()); QString name = QString::fromStdString(req.name()); // Linux doesn't like it when there are non plausable charaters name = name.replace(QRegExp(QStringLiteral("[^ -~]")), QLatin1String("")); name = name.trimmed(); if (!name.isEmpty()) req.name(name.toStdString()); pos += 8; LtsUtils::get1Byte(data, pos, tmp); if (tmp != 0) req.partyId(dds::core::optional(tmp)); else req.partyId(dds::core::optional()); if (!withoutEffectStart) { LtsUtils::get4Byte(data, pos, tmp); if (tmp != 0) req.effectStart(dds::core::optional(tmp * 1000)); else req.effectStart(dds::core::optional()); } } // mathematical negativ east = 0° -> mathematical positiv, north = 0°, 1/100° as int -> factor 100 static inline double mathToCompassAngle(int mathDegree) { int ret = 9000 - mathDegree; while (ret < 0) ret += 36000; while (ret > 36000) ret -= 36000; return ret / 100.0; } void HandlerAesData::bcContaminations(const QByteArray &data) { INFO_LOG(m_logger) << "Got new BC contamination data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); QList aesToBeDeleted = m_aesDtc->bCContaminations().keys(); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); AreaEffectSimulation::BCContaminationModifyRequest req; req.weatherSource(AreaEffectSimulation::AesWeatherSource::AwsNoUpdate); uint64_t tmp; int x = 0, y = 0; for (int i = 0; i < cnt; ++i) { getBasicData(data, pos, req); LtsUtils::get1Byte(data, pos, tmp); req.effectDuration(tmp * 60 * 1000); LtsUtils::get3Byte(data, pos, y); // north LtsUtils::get3Byte(data, pos, x); // east req.impactPosition(relativeToGeoPosition(x, y)); LtsUtils::get2Byte(data, pos, tmp); req.impactRadius(tmp); LtsUtils::get1Byte(data, pos, tmp); // expanding factor -> is deduced from ChargeType LtsUtils::get2Byte(data, pos, tmp); // wind direction req.propagationDirection(dds::core::optional(compassToMathAngleForSfm(tmp))); // what an angle LtsUtils::get1Byte(data, pos, tmp); // wind speed req.windSpeed(dds::core::optional(tmp)); LtsUtils::get4Byte(data, pos, tmp); // timestamp since when weather is valid req.weatherTimestamp(dds::core::optional(tmp * 1000)); // secs to msecs LtsUtils::get3Byte(data, pos, y); // north when weather changed LtsUtils::get3Byte(data, pos, x); // east when weather changed req.weatherPosition(dds::core::optional(relativeToGeoPosition(x, y))); LtsUtils::get1Byte(data, pos, tmp); req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Chemical, tmp)); if (req.chargeTypeId() == 0) req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Biological, tmp)); LtsUtils::get1Byte(data, pos, tmp); // votality type -> is deduced from ChargeType DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "BC contamination: " << req; aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted req.id(req.id() * -1); // inverted id to mark as for oc m_aesFtc->bCContaminationModifyRequest(req, this); } for (int id : qAsConst(aesToBeDeleted)) { if (m_aesDtc->bCContamination(id).isCentral()) continue; if (m_aesDtc->bCContamination(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved) { DEBUG_LOG(m_logger) << "Removing BC contamination id: " << id; m_aesFtc->bCContaminationRemoveRequest(Core::RemoveElementRequest(id), this); } } } void HandlerAesData::explosionAreas(const QByteArray &data) { INFO_LOG(m_logger) << "Got new explosion cluster data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); QList aesToBeDeleted = m_aesDtc->explosionClusters().keys(); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); AreaEffectSimulation::ExplosionClusterOcModifyRequest req; uint64_t tmp; int x = 0, y = 0; for (int i = 0; i < cnt; ++i) { // basic data LtsUtils::get2Byte(data, pos, tmp); // id req.id(tmp); req.name(data.mid(pos, 8).toStdString()); // name QString name = QString::fromStdString(req.name()); // Linux doesn't like it when there are non plausable charaters name = name.replace(QRegExp(QStringLiteral("[^ -~]")), QLatin1String("")); name = name.trimmed(); if (!name.isEmpty()) req.name(name.toStdString()); pos += 8; LtsUtils::get1Byte(data, pos, tmp); // party id req.partyId(tmp); LtsUtils::get4Byte(data, pos, tmp); // effect start req.effectStart(tmp * 1000); LtsUtils::get2Byte(data, pos, tmp); // effect duration req.effectDuration(tmp * 1000); // area AreaEffectSimulation::Area area; LtsUtils::get3Byte(data, pos, y); // north LtsUtils::get3Byte(data, pos, x); // east area.cornerBottomLeft(relativeToGeoPosition(x, y)); LtsUtils::get1Byte(data, pos, tmp); area.width(tmp * 5); LtsUtils::get1Byte(data, pos, tmp); area.height(tmp * 5); LtsUtils::get2Byte(data, pos, tmp); area.rotation(mathToCompassAngle(tmp + 9000)); // +9000, as other edge is used as reference to rotate in the sfm req.area(area); LtsUtils::get2Byte(data, pos, tmp); // mhp width (1 byte) and length (1 byte) --> just read and skip, since it is not needed here LtsUtils::get2Byte(data, pos, tmp); // mhp rotation req.mhpRotation(mathToCompassAngle(tmp + 9000)); // +9000, as other edge is used as reference to rotate in the sfm LtsUtils::get2Byte(data, pos, tmp); // impact count req.impactCount(tmp); LtsUtils::get2Byte(data, pos, tmp); // impact edge length --> just read and skip, since it is not needed here LtsUtils::get1Byte(data, pos, tmp); // charge type id req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Artillery, tmp)); if (req.chargeStruct().chargeTypeId() == 0) req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Mortar, tmp)); if (req.chargeStruct().chargeTypeId() == 0) req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::AirSupport, tmp)); LtsUtils::get2Byte(data, pos, tmp); // hazard distance (1 byte) and hazard type (1 byte) --> just read and skip, since it is not needed here LtsUtils::get1Byte(data, pos, tmp); // mhp count req.mhpCount(tmp); // mhps --> just read and skip, since it is not needed here for (int32_t mhpCounter = 0; mhpCounter < req.mhpCount(); ++mhpCounter) { LtsUtils::get3Byte(data, pos, tmp); LtsUtils::get3Byte(data, pos, tmp); } DEBUG_LOG(m_logger) << "New ExplosionClusterOcModifyRequest: " << "\nid:" << req.id() << "\nname:" << req.name() << "\neffectStart:" << req.effectStart() << "\neffectDuration:" << req.effectDuration() << "\narea:" << req.area() << "\nmhpCount:" << req.mhpCount() << "\nmhpRotation:" << req.mhpRotation() << "\nimpactCount:" << req.impactCount() << "\nchargeTypeId:" << req.chargeStruct().chargeTypeId() << "\npartyId:" << req.partyId(); DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "explosion cluster: " << req; aesToBeDeleted.removeOne(req.id()); // item was created/modified -> does not need to be deleted req.id(req.id() * -1); // inverted id to mark as for oc m_aesFtc->explosionClusterOcModifyRequest(req, this); } for (int id : qAsConst(aesToBeDeleted)) { const AreaEffectSimulation::ExplosionCluster &exp = m_aesDtc->explosionCluster(id); if (exp.isCentral()) continue; if ((exp.effectStateId().underlying() >= AreaEffectSimulation::AesEffectState::EsPrepared) || ((exp.effectStateId().underlying() >= AreaEffectSimulation::AesEffectState::EsActivated) && (((exp.effectStart().is_set() && ((exp.effectStart().get() + exp.effectDuration())) < (m_dds->currentPartitionTime() - 5000))) || // is not recently expired (exp.effectStateId().underlying() == AreaEffectSimulation::AesEffectState::EsPaused)))) // or is paused { DEBUG_LOG(m_logger) << "Removing explosion cluster with id: " << id; m_aesFtc->explosionClusterRemoveRequest(Core::RemoveElementRequest(id), this); } } } void HandlerAesData::explosionSingles(const QByteArray &data) { INFO_LOG(m_logger) << "Got new explosion single data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); QList aesToBeDeleted = m_aesDtc->explosionSingles().keys(); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); AreaEffectSimulation::ExplosionSingleModifyRequest req; uint64_t tmp; int x = 0, y = 0; for (int i = 0; i < cnt; ++i) { getBasicData(data, pos, req); LtsUtils::get3Byte(data, pos, y); // north LtsUtils::get3Byte(data, pos, x); // east req.impactPosition(relativeToGeoPosition(x, y)); if (m_use2BytesForSingleFireDiameter) LtsUtils::get2Byte(data, pos, tmp); // impact radius -> is deduced from ChargeType else LtsUtils::get1Byte(data, pos, tmp); // impact radius -> is deduced from ChargeType LtsUtils::get1Byte(data, pos, tmp); req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Artillery, tmp)); if (req.chargeTypeId() == 0) req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Mortar, tmp)); DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "explosion single: " << req; aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted req.id(req.id() * -1); // inverted id to mark as for oc m_aesFtc->explosionSingleModifyRequest(req, this); } for (int id : qAsConst(aesToBeDeleted)) { const AreaEffectSimulation::ExplosionSingle &exp = m_aesDtc->explosionSingle(id); if (exp.isCentral()) continue; if ((exp.effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved) && (exp.effectStart().is_set() && (exp.effectStart().get() > (m_dds->currentPartitionTime() + 5000)))) // is not about to expire { DEBUG_LOG(m_logger) << "Removing explosion single id: " << id; m_aesFtc->explosionSingleRemoveRequest(Core::RemoveElementRequest(id), this); } } } void HandlerAesData::functionalAreas(const QByteArray &data) { INFO_LOG(m_logger) << "Got functional area data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); QList aesToBeDeleted = m_aesDtc->functionalAreas().keys(); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); AreaEffectSimulation::FunctionalAreaModifyRequest req; uint64_t tmp; int x = 0, y = 0; for (int i = 0; i < cnt; ++i) { getBasicData(data, pos, req, true); if (!aesToBeDeleted.contains(req.id())) // is a create? req.effectStart(dds::core::optional(m_dds->currentPartitionTime() + 100)); // functional areas always start on create // area AreaEffectSimulation::Area area; LtsUtils::get3Byte(data, pos, y); // north LtsUtils::get3Byte(data, pos, x); // east area.cornerBottomLeft(relativeToGeoPosition(x, y)); LtsUtils::get1Byte(data, pos, tmp); area.width(tmp * 5); LtsUtils::get1Byte(data, pos, tmp); area.height(tmp * 5); area.rotation(0); req.area(area); // modes and times LtsUtils::get1Byte(data, pos, tmp); req.deconMode(static_cast(tmp)); LtsUtils::get1Byte(data, pos, tmp); req.deconTime(tmp * 10 * 60 * 1000); // to ms LtsUtils::get1Byte(data, pos, tmp); req.maintMode(static_cast(tmp)); LtsUtils::get1Byte(data, pos, tmp); req.maintTime(tmp * 10 * 60 * 1000); // to ms LtsUtils::get1Byte(data, pos, tmp); req.medicMode(static_cast(tmp)); LtsUtils::get1Byte(data, pos, tmp); req.medicTime(tmp * 10 * 60 * 1000); // to ms req.chargeTypeId(411); // Dummy DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "functional area: " << req; aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted req.id(req.id() * -1); // inverted id to mark as for oc m_aesFtc->functionalAreaModifyRequest(req, this); } for (int id : qAsConst(aesToBeDeleted)) { if (m_aesDtc->functionalArea(id).isCentral()) continue; if (m_aesDtc->functionalArea(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved) { DEBUG_LOG(m_logger) << "Removing functional area id: " << id; m_aesFtc->functionalAreaRemoveRequest(Core::RemoveElementRequest(id), this); } } } void HandlerAesData::nContaminations(const QByteArray &data) { INFO_LOG(m_logger) << "Got new N contamination data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); QList aesToBeDeleted = m_aesDtc->nContaminations().keys(); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); AreaEffectSimulation::NContaminationModifyRequest req; uint64_t tmp; int x = 0, y = 0; for (int i = 0; i < cnt; ++i) { getBasicData(data, pos, req); LtsUtils::get2Byte(data, pos, tmp); req.effectDuration(tmp * 6 * 60 * 1000); LtsUtils::get3Byte(data, pos, y); // north LtsUtils::get3Byte(data, pos, x); // east req.impactPosition(relativeToGeoPosition(x, y)); LtsUtils::get2Byte(data, pos, tmp); req.impactRadius(tmp); LtsUtils::get2Byte(data, pos, tmp); req.borderLengthInner(tmp); LtsUtils::get2Byte(data, pos, tmp); req.borderLengthOuter(tmp); LtsUtils::get2Byte(data, pos, tmp); req.spanAngle(tmp / 100.0); LtsUtils::get2Byte(data, pos, tmp); req.rotationAngle(mathToCompassAngle(tmp)); req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Nuclear, 1)); DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "N contamination: " << req; aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted req.id(req.id() * -1); // inverted id to mark as for oc m_aesFtc->nContaminationModifyRequest(req, this); } for (int id : qAsConst(aesToBeDeleted)) { if (m_aesDtc->nContamination(id).isCentral()) continue; if (m_aesDtc->nContamination(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved) { DEBUG_LOG(m_logger) << "Removing N contamination id: " << id; m_aesFtc->nContaminationRemoveRequest(Core::RemoveElementRequest(id), this); } } } void HandlerAesData::mineAreas(const QByteArray &data) { INFO_LOG(m_logger) << "Got new mine area data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); QList aesToBeDeleted = m_aesDtc->mineAreas().keys(); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); AreaEffectSimulation::MineArea req; uint64_t tmp; int x = 0, y = 0; for (int i = 0; i < cnt; ++i) { getBasicData(data, pos, req); LtsUtils::get1Byte(data, pos, tmp); req.effectDuration(tmp * 60 * 60 * 1000); // convert to msecs LtsUtils::get1Byte(data, pos, tmp); req.width(tmp); LtsUtils::get1Byte(data, pos, tmp); req.density(tmp / 100.0); LtsUtils::get1Byte(data, pos, tmp); if (217 > tmp && 210 < tmp) // Legacy Ids req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::MineArea, tmp)); else // OSAG = ChargeTypeIs req.chargeStruct().chargeTypeId(tmp); LtsUtils::get2Byte(data, pos, tmp); // total mine count // segments int segmentCnt = 0; LtsUtils::get1Byte(data, pos, segmentCnt); req.segments().resize(segmentCnt); for (int j = 0; j < segmentCnt; ++j) { AreaEffectSimulation::MineSegment &segment = req.segments().at(j); LtsUtils::get3Byte(data, pos, y); LtsUtils::get3Byte(data, pos, x); segment.positionStart(relativeToGeoPosition(x, y)); LtsUtils::get3Byte(data, pos, y); LtsUtils::get3Byte(data, pos, x); segment.positionEnd(relativeToGeoPosition(x, y)); } req.isCenterLine(true); DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "mine area: " << req; aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted req.id(req.id() * -1); // inverted id to mark as for oc req.setMineSorted(false); m_aesFtc->mineAreaModifyFromCtaDataRequest(req, this); } for (int id : qAsConst(aesToBeDeleted)) { if (m_aesDtc->mineArea(id).isCentral()) continue; if (m_aesDtc->mineArea(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved) { DEBUG_LOG(m_logger) << "Removing mine area id: " << id; m_aesFtc->mineAreaRemoveRequest(Core::RemoveElementRequest(id), this); } } } void HandlerAesData::explosionAreasPause(const QByteArray &data) { INFO_LOG(m_logger) << "Got new explosion area pause data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); uint64_t id; for (int i = 0; i < cnt; ++i) { LtsUtils::get2Byte(data, pos, id); DEBUG_LOG(m_logger) << "Pausing ID: " << id; m_aesFtc->explosionAreaPauseResumeRequest(AreaEffectSimulation::ExplosionAreaPauseResumeRequest(id, false, dds::core::optional(), dds::core::optional(), dds::core::optional()), this); } } void HandlerAesData::explosionAreasResume(const QByteArray &data) { INFO_LOG(m_logger) << "Got new explosion area resume data"; DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' '); size_t pos = 0; int cnt; LtsUtils::get1Byte(data, pos, cnt); uint64_t tmp; AreaEffectSimulation::ExplosionAreaPauseResumeRequest req; req.resumeExecution(true); for (int i = 0; i < cnt; ++i) { LtsUtils::get2Byte(data, pos, tmp); req.id(tmp); LtsUtils::get2Byte(data, pos, tmp); req.resumeSeed(dds::core::optional(tmp)); LtsUtils::get2Byte(data, pos, tmp); // number of impact -> not used, calculated internally LtsUtils::get4Byte(data, pos, tmp); req.timeToResume(dds::core::optional(tmp * 1000)); LtsUtils::get2Byte(data, pos, tmp); req.remainingDuration(dds::core::optional(tmp)); DEBUG_LOG(m_logger) << "Resuming: " << req; m_aesFtc->explosionAreaPauseResumeRequest(req, this); } } int HandlerAesData::chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass gearClass, int chargeTasCodeIndirect) { static bool initialized = false; if (!initialized) { if (!m_pdmDtc->waitForHistoricalDataChargeType(20000)) // 20s { ERROR_LOG(m_logger) << "Can not read charge types from PDM"; QCoreApplication::exit(-1); return -1; } initialized = true; } return m_pdmDtc->chargeTypeIdFromLegacyId(gearClass, chargeTasCodeIndirect); } Core::GeoPosition2D HandlerAesData::relativeToGeoPosition(int x, int y) const { const QPointF point(m_utmCoord.geographic(x, y)); return Core::GeoPosition2D(point.x(), point.y()); } } // namespace TrainingAreaBaseOC