/** * @RDE_HEADER@ * ConfigCnr * $Id$ * $HeadURL$ * $Author$ * Created by Christian Fröbel, NLE, 2019-08-05 */ #include "config_cnr.h" #include "helper.h" #include #include #include #include #include #include #include #include namespace CombatNetRadio { ConfigCnr::ConfigCnr(int exerciseId, AudioVideoManagement::Interface::Dtc *avmDtc, InfrastructureManagement::Interface::Dtc *ismDtc) : m_exerciseId(exerciseId) , m_avmDtc(avmDtc) , m_ismDtc(ismDtc) , m_logger(getLogger("ConfigCnr")) , m_loggerJson(getLogger("ConfigCnr.Json")) { Q_ASSERT(avmDtc); Q_ASSERT(ismDtc); m_timer.setSingleShot(true); m_timer.setInterval(200); connect(&m_timer, &QTimer::timeout, this, &ConfigCnr::buildConfigCnrData); connect(m_avmDtc, &AudioVideoManagement::Interface::Dtc::activeFrequencyPlanningChanged, this, &ConfigCnr::onActiveFrequencyPlanningChanged); connect(m_avmDtc, &AudioVideoManagement::Interface::Dtc::frequencyPlanningChanged, this, &ConfigCnr::onFrequencyPlanningChanged); connect(m_avmDtc, &AudioVideoManagement::Interface::Dtc::cnrConfigChanged, this, &ConfigCnr::onCnrConfigChanged); buildConfigCnrData(); } const WebSocketInterface::Messages::ConfigCnrMessage::ConfigCnrData &ConfigCnr::data() const { Q_ASSERT(hasActiveFrequencyPlanning()); return m_configCnrData; } void ConfigCnr::buildConfigCnrData() { WebSocketInterface::Messages::ConfigCnrMessage::ConfigCnrData configCnr; boost::optional plan = activePlan(); if (!plan) { m_configCnrData = configCnr; emit changed(); return; } AudioVideoManagement::FrequencySet frequencySet = m_avmDtc->frequencySet(plan->frequencySetId()); QMap fsdMap; for (const auto &fsd : qAsConst(frequencySet.data())) fsdMap.insert(fsd.id(), fsd); configCnr.setId = plan->frequencySetId(); configCnr.time = QDateTime::fromMSecsSinceEpoch(plan->date()).toString(Qt::ISODate); configCnr.radioCircuit.reserve(plan->data().size()); for (const auto ¤tData : plan->data()) { WebSocketInterface::Messages::ConfigCnrMessage::RadioCircuit radioCircuit; AudioVideoManagement::CnrConfig cnrConfig = m_avmDtc->cnrConfig(currentData.cnrConfigId()); AudioVideoManagement::CnrChannel cnrChannel = m_avmDtc->cnrChannel(cnrConfig.cnrChannelId()); QVector circuits = AudioVideoManagement::fromJson(currentData.properties()); int subCircuitCount = 0; for (AudioVideoManagement::RadioCircuit* circuit : qAsConst(circuits)) { InfrastructureManagement::MainStructureElement radioStation = m_ismDtc->mainStructureElement(circuit->radioStationId); WebSocketInterface::Messages::ConfigCnrMessage::SubRadioCircuit subCircuit; switch (circuit->freqBand.underlying()) { case AudioVideoManagement::FrequencyBand_def::VHF: { AudioVideoManagement::RadioCircuitVHF *rcVhf = dynamic_cast(circuit); if (!rcVhf) break; AudioVideoManagement::FrequencySetDataWithId &fsd = fsdMap[rcVhf->freqSetDataId]; subCircuit.agc = false; subCircuit.akw = fsd.akwBundle().is_set() ? fsd.akwBundle().get() : 0; subCircuit.channelSpacing = 0; subCircuit.clipper = false; subCircuit.comsec = fsd.comsec().is_set() ? fsd.comsec().get() : 0; subCircuit.crossbeam = 0; subCircuit.frequency = fsd.frequency() * 1000; // kHz --> Hz subCircuit.frequencyBand = fsd.frequencyBand().underlying(); subCircuit.fsb = fsd.fsbBundle().is_set() ? fsd.fsbBundle().get() : 0; subCircuit.modulationType = 0; subCircuit.orientation = 0; subCircuit.ptSig = rcVhf->pilotTone; subCircuit.rcAkw = fsd.akwRadioCircuit().is_set() ? fsd.akwRadioCircuit().get() : 0; subCircuit.rcMode = rcVhf->operatingMode.underlying(); subCircuit.rcSubId = toRcIdString(cnrChannel); subCircuit.rcSubName = rcVhf->name; subCircuit.rcType = 2;//circuit->freqBand.underlying(); subCircuit.squelch = 0; subCircuit.station = stationId(radioStation); break; } case AudioVideoManagement::FrequencyBand_def::UHF: { AudioVideoManagement::RadioCircuitUHF *rcUhf = dynamic_cast(circuit); if (!rcUhf) break; subCircuit.agc = rcUhf->agc; subCircuit.akw = 0; subCircuit.channelSpacing = rcUhf->channelSpacing.underlying(); subCircuit.clipper = rcUhf->clipper; subCircuit.comsec = 0; subCircuit.crossbeam = 0; subCircuit.frequency = rcUhf->frequency; // Really? ICD says different subCircuit.frequencyBand = 0; // == rcType? subCircuit.fsb = 0; subCircuit.modulationType = rcUhf->modulationType.underlying(); subCircuit.orientation = 0; subCircuit.ptSig = 0; subCircuit.rcAkw = 0; subCircuit.rcMode = 0; // VHF only? subCircuit.rcSubId = toRcIdString(cnrChannel); subCircuit.rcSubName = rcUhf->name; subCircuit.rcType = 3;//circuit->freqBand.underlying(); subCircuit.squelch = rcUhf->squelchDB; subCircuit.station = stationId(radioStation); break; } case AudioVideoManagement::FrequencyBand_def::HF: { AudioVideoManagement::RadioCircuitHF *rcHf = dynamic_cast(circuit); if (!rcHf) break; subCircuit.agc = 0; subCircuit.akw = 0; subCircuit.channelSpacing = rcHf->channelSpacing.underlying(); subCircuit.clipper = 0; subCircuit.comsec = 0; subCircuit.crossbeam = 0; subCircuit.frequency = rcHf->frequency; // Really? ICD says different subCircuit.frequencyBand = 0; // == rcType? subCircuit.fsb = 0; subCircuit.modulationType = rcHf->modulationType.underlying(); subCircuit.orientation = 0; subCircuit.ptSig = 0; subCircuit.rcAkw = 0; subCircuit.rcMode = 0; // VHF only? subCircuit.rcSubId = toRcIdString(cnrChannel); subCircuit.rcSubName = rcHf->name; subCircuit.rcType = 1;//circuit->freqBand.underlying(); subCircuit.squelch = rcHf->squelch; subCircuit.station = stationId(radioStation); break; } default: break; } if (circuits.count() > 1) subCircuit.rcSubId.append(QChar('A' + subCircuitCount++)); radioCircuit.subCircuits.append(subCircuit); } qDeleteAll(circuits); radioCircuit.rcId = toRcIdString(cnrChannel); radioCircuit.rcName = QString::fromStdString(cnrConfig.radioCircuit()); radioCircuit.ssrc = cnrChannel.ssrc(); configCnr.radioCircuit.append(radioCircuit); } // NOTICE_LOG(m_loggerJson) << WebSocketInterface::Messages::ConfigCnrMessage(configCnr, QUuid::createUuid().toString()).toJsonString(); m_configCnrData = configCnr; emit changed(); } boost::optional ConfigCnr::activePlan() const { AudioVideoManagement::ActiveFrequencyPlanning activePlanning = m_avmDtc->activeFrequencyPlanning(m_exerciseId); if (activePlanning.exerciseId() != m_exerciseId) return boost::none; AudioVideoManagement::FrequencyPlanning frequencyPlanning = m_avmDtc->frequencyPlanning(activePlanning.frequencyPlanningId()); return frequencyPlanning; } void ConfigCnr::onActiveFrequencyPlanningChanged(const AudioVideoManagement::ActiveFrequencyPlanning &data, DataModification modType) { DEBUG_LOG(m_logger) << LOGFUNCTION << ": " << data; Q_UNUSED(modType); if (data.exerciseId() == m_exerciseId) m_timer.start(); } void ConfigCnr::onFrequencyPlanningChanged(const AudioVideoManagement::FrequencyPlanning &data, DataModification modType) { DEBUG_LOG(m_logger) << LOGFUNCTION << ": " << data; Q_UNUSED(modType); boost::optional plan = activePlan(); if (!plan) return; if (data.id() == plan->id() || !data.exerciseId().is_set()) m_timer.start(); } //Daten nur rausschicken, wenn cnrConfig Data gerade in einer aktiven Übung verwendet wird void ConfigCnr::onCnrConfigChanged(const AudioVideoManagement::CnrConfig &data, DataModification modType) { DEBUG_LOG(m_logger) << LOGFUNCTION << ": " << data; Q_UNUSED(modType); bool isGlobal = !data.exerciseId().is_set(); if ((isGlobal || data.exerciseId().get() == m_exerciseId) && modType != DataModification::eAdded && isInActiveFrequencyPlanning(data)) m_timer.start(); } //todo: wird das noch wo anders benötigt? bool ConfigCnr::isInActiveFrequencyPlanning(const AudioVideoManagement::CnrConfig &data) { //globale Änderung immer schicken if(!data.exerciseId().is_set()) return true; const auto activePlans = m_avmDtc->activeFrequencyPlannings(); for (const AudioVideoManagement::ActiveFrequencyPlanning &activePlan : activePlans) { const auto frequencyPlannings = m_avmDtc->frequencyPlanning(activePlan.frequencyPlanningId()); for(const auto &frequencyPlanningData : frequencyPlannings.data()) { if(frequencyPlanningData.cnrConfigId() == data.id()) return true; } } return false; } } // namespace CombatNetRadio