Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,98 @@
/**
* @RDE_HEADER@
* Helper funtcions for converting CnrConfigs
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#include "avm_cnr_json_helper.h"
#include <AudioVideoManagement/avm_dtc.h>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <FileManagement/flm_translation.h>
namespace AudioVideoManagement
{
QByteArray cnrConfigVectorToJson(const QVector<CnrConfig> &configVector, int32_t exerciseId)
{
const bool isGlobal = exerciseId == 0;
QJsonObject obj;
QJsonArray arr;
for (const CnrConfig &config : qAsConst(configVector))
{
if((isGlobal && config.exerciseId().is_set())
|| (!isGlobal && !config.exerciseId().is_set()))
continue;
arr += cnrConfigToJson(config);
}
obj.insert(JsonKeys::XML_TYPE(), FileManagement::toString(FileManagement::XmlType::AVMCombatNetRadio));
obj.insert(JsonKeys::CNR_CONFIGURATION(), arr);
const QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
QJsonObject cnrConfigToJson(const CnrConfig &config)
{
QJsonObject cnrObj;
using namespace JsonKeys;
cnrObj.insert(CALL_SIGN(), QString::fromStdString(config.callSign()));
cnrObj.insert(CNR_CHANNEL_ID(), config.cnrChannelId());
cnrObj.insert(IS_GLOBAL(), !config.exerciseId().is_set());
cnrObj.insert(RADIO_CIRCUIT(), QString::fromStdString(config.radioCircuit()));
cnrObj.insert(IS_ACTIVE(), config.isActive());
cnrObj.insert(RECORDING(), config.recording());
return cnrObj;
}
template<typename T>
T jsonToCnr(const QJsonObject &cnrJson)
{
T cnrConfigRequest;
using namespace JsonKeys ;
cnrConfigRequest.callSign(cnrJson.value(CALL_SIGN()).toString().toStdString());
cnrConfigRequest.cnrChannelId(cnrJson.value(CNR_CHANNEL_ID()).toInt());
cnrConfigRequest.id(0);
cnrConfigRequest.radioCircuit(cnrJson.value(RADIO_CIRCUIT()).toString().toStdString());
cnrConfigRequest.recording(cnrJson.value(RECORDING()).toBool());
return cnrConfigRequest;
}
QVector<CnrConfig> jsonToCnrConfigVector(const QByteArray &data)
{
using namespace JsonKeys;
const QJsonDocument doc(QJsonDocument::fromJson(data));
const QJsonObject obj(doc.object());
const QJsonArray arr(obj.value(CNR_CONFIGURATION()).toArray());
QVector<CnrConfig> result;
result.reserve(arr.size());
for (const auto &elem : arr)
{
const QJsonObject obj(elem.toObject());
result.append(jsonToCnr<CnrConfig>(obj));
}
return result;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* Helper funtcions for converting CnrConfigs
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#pragma once
#include <AudioVideoManagement/avm_dtc.h>
namespace AudioVideoManagement
{
namespace JsonKeys
{
inline QString CALL_SIGN() { return QLatin1String("callSign"); }
inline QString CNR_CHANNEL_ID() { return QLatin1String("cnrChannelId"); }
inline QString CNR_CONFIGURATION() { return QLatin1String("cnrConfiguration"); }
inline QString IS_ACTIVE() { return QLatin1String("isActive"); }
inline QString IS_GLOBAL() { return QLatin1String("isGlobal"); }
inline QString RADIO_CIRCUIT() { return QLatin1String("radioCircuit"); }
inline QString RECORDING() { return QLatin1String("recording"); }
inline QString XML_TYPE() { return QLatin1String("XmlType"); }
} // namespace JsonKeys
QByteArray cnrConfigVectorToJson(const QVector<CnrConfig> &configVector, int32_t exerciseId);
QJsonObject cnrConfigToJson(const CnrConfig &config);
QVector<CnrConfig> jsonToCnrConfigVector(const QByteArray &data);
template <typename T> // CnrConfig or ModifyCnrConfigRequest
T jsonToCnr(const QJsonObject &cnrJson);
} // namespace AudioVideoManagement
@@ -0,0 +1,259 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialog shows all CNR configurations and offers options for creation and editing
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#include "avm_combat_net_radio_dialog.h"
#include "ui_avm_combat_net_radio_dialog.h"
#include "avm_combat_net_radio_dialog_model.h"
#include "avm_edit_combat_net_radio_dialog.h"
#include "avm_cnr_json_helper.h"
#include "avm_load_combat_net_radio_dialog.h"
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsSession/lts_session.h>
#include <LtsPrinting/printing.h>
#include <QTreeView>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
namespace AudioVideoManagement
{
CombatNetRadioDialog::CombatNetRadioDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_session(session)
, m_ui(new Ui::CombatNetRadioDialog)
, m_menuGroup(menuGroup)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_logger(getLogger("CombatNetRadioDialog"))
{
m_ui->setupUi(this);
setUnchangeable(true);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &CombatNetRadioDialog::onPrint);
setOnlineHelpFilename(QStringLiteral("Situation-Combat_Net_Radio_Configuration"));
}
CombatNetRadioDialog::~CombatNetRadioDialog() = default;
void CombatNetRadioDialog::retranslate()
{
translate(m_ui.get(), tr("Configure Combat Net Radio"));
}
void CombatNetRadioDialog::tableColumnOrganiserSetting()
{
//LtsColumnOrganiser
m_ui->dataView->setColumnsOrganiserEnabled(identifier(), true);
}
bool CombatNetRadioDialog::init(const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
ExerciseManagement::Interface::Dtc *exmDtc)
{
if (!(avmDtc->enableCnrChannel() &&
avmDtc->enableCnrConfig() &&
ppmDtc->enableParty() &&
ppmDtc->enableParticipant() &&
ppmDtc->enableType() &&
avmFtc->enableModifyCnrConfig() &&
avmFtc->enableRemoveCnrConfig()))
{
ERROR_LOG(m_logger) << "Could not enable topics";
return false;
}
m_model.reset(new CombatNetRadioDialogModel(m_session->exerciseId(), this));
m_filter.reset(new CombatNetRadioDialogFilter(m_session->exerciseId(), m_model.get()));
m_avmDtc = avmDtc;
m_avmFtc = avmFtc;
m_ppmDtc = ppmDtc;
m_exmDtc = exmDtc;
m_avmDtc->waitForHistoricalDataCnrChannel(3000);
m_ppmDtc->waitForHistoricalDataParty(3000);
if (!m_model->init(avmDtc, ppmDtc))
{
m_filter.reset();
m_model.reset();
ERROR_LOG(m_logger) << "Could not init model";
return false;
}
m_ui->dataView->setModel(m_filter.get());
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &CombatNetRadioDialog::onAddClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &CombatNetRadioDialog::onEditClicked);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &CombatNetRadioDialog::onRemoveClicked);
connect(m_ui->dataView, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &CombatNetRadioDialog::onSelectionChanged);
onSelectionChanged();
connect(m_ui->dataView, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &CombatNetRadioDialog::onEditClicked);
// Save/Load from Library Button
m_buttonBox->connectLibraryButton(this,
&CombatNetRadioDialog::onImportButtonClicked,
&CombatNetRadioDialog::onExportButtonClicked);
if (!m_session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui))
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Library, false);
return true;
}
void CombatNetRadioDialog::onRemoveCnrConfigReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void CombatNetRadioDialog::onModifyCnrConfigReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void CombatNetRadioDialog::onAddClicked()
{
EditCombatNetRadioDialog *dialog = new EditCombatNetRadioDialog(m_session->exerciseId(), m_session, m_ppmDtc, m_avmDtc, m_avmFtc, m_exmDtc, this);
addDialog(dialog, m_menuGroup, QStringLiteral("NewCNRConfig"), LtsWidgets::LayoutArea::eDialogTab);
}
void CombatNetRadioDialog::onEditClicked()
{
// avoid double click on table:
if (!m_buttonBox->isButtonEnabled(LtsWidgets::DialogButton::Edit))
return;
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
CnrConfig config = m_model->data(currentIndex, Qt::UserRole).value<CnrConfig>();
EditCombatNetRadioDialog *dialog = new EditCombatNetRadioDialog(config,
m_session->exerciseId(),
m_session,
m_ppmDtc,
m_avmDtc,
m_avmFtc,
m_exmDtc,
this);
addDialog(dialog, m_menuGroup, QStringLiteral("EditCNRConfig%1").arg(config.id()), LtsWidgets::LayoutArea::eDialogTab);
}
void CombatNetRadioDialog::onRemoveClicked()
{
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
Core::RemoveElementRequest request(m_model->data(currentIndex, Qt::UserRole).value<CnrConfig>().id());
m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->removeCnrConfigRequest(request, this);
});
}
void CombatNetRadioDialog::onSelectionChanged()
{
bool rowSelected = m_ui->dataView->selectionModel()->selectedRows().size() > 0;
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, rowSelected);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, rowSelected);
QModelIndex currentIndex = m_ui->dataView->currentIndex();
if (!currentIndex.isValid())
return;
CnrConfig config = m_model->data(currentIndex, Qt::UserRole).value<CnrConfig>();
bool enabled = rowSelected && (config.exerciseId().is_set() || m_session->partitionType() == IDL::PartitionType::Global);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, enabled);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, enabled);
}
void CombatNetRadioDialog::onPrint()
{
LtsPrinting::printView(m_session, this, m_ui->dataView->treeView());
}
void CombatNetRadioDialog::onExportButtonClicked()
{
QByteArray byteArray = m_filter->modelToJson();
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_session->exerciseId(), byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void CombatNetRadioDialog::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << " onImportButtonClicked() ";
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_session->exerciseId(), FileManagement::toString(FileManagement::XmlType::AVMCombatNetRadio));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMCombatNetRadio)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
LoadCombatNetRadioDialog loadingDialog(content, m_session) ;
loadingDialog.exec();
if (loadingDialog.isApplied())
{
const QVector<Core::RemoveElementRequest> removeRequests = loadingDialog.removeRequests();
for (const auto &removeRequest : removeRequests)
{
m_requestHelper->addRequest([this, removeRequest]()
{
return m_avmFtc->removeCnrConfigRequest(removeRequest, this);
});
}
const QVector<ModifyCnrConfigRequest> modifyRequests = loadingDialog.modifyRequests();
for (const auto &modifyRequest : modifyRequests)
{
m_requestHelper->addRequest([this, modifyRequest]()
{
return m_avmFtc->modifyCnrConfigRequest(modifyRequest, this);
});
}
if (modifyRequests.size() || removeRequests.size())
(void) m_requestHelper->startRequests();
}
}
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,107 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialog shows all CNR configurations and offers options for creation and editing
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <memory>
namespace Ui
{
class CombatNetRadioDialog;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
class CombatNetRadioDialogModel;
class CombatNetRadioDialogFilter;
class CombatNetRadioDialog
: public LtsWidgets::LtsDialog
, protected Interface::Ftc::RemoveCnrConfigReplyHandler
, protected Interface::Ftc::ModifyCnrConfigReplyHandler
{
Q_OBJECT
public:
explicit CombatNetRadioDialog(LtsSession::LtsSession *session, const QString &menuGroup, QWidget *parent = nullptr);
~CombatNetRadioDialog();
bool init(const std::shared_ptr<Interface::Dtc> &avmDtc, const std::shared_ptr<Interface::Ftc> &avmFtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, ExerciseManagement::Interface::Dtc *exmDtc);
void onRemoveCnrConfigReply(const Core::FunctionReplyObject &replyObject) override final;
void onModifyCnrConfigReply(const Core::FunctionReplyObject &replyObject) override final;
private Q_SLOTS:
void onAddClicked();
void onEditClicked();
void onRemoveClicked();
void onSelectionChanged();
void onPrint();
void onExportButtonClicked();
void onImportButtonClicked();
private: // methods
void retranslate() override;
void tableColumnOrganiserSetting() override;
private: // attributes
LtsSession::LtsSession *m_session;
std::unique_ptr<CombatNetRadioDialogModel> m_model;
std::unique_ptr<CombatNetRadioDialogFilter> m_filter;
std::unique_ptr<Ui::CombatNetRadioDialog> m_ui;
QString m_menuGroup;
std::unique_ptr<LtsUtils::RequestHelper> m_requestHelper;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<Interface::Ftc> m_avmFtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
ExerciseManagement::Interface::Dtc *m_exmDtc{nullptr};
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
LOGGER_T m_logger;
}; // class CombatNetRadioDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CombatNetRadioDialog</class>
<widget class="QWidget" name="CombatNetRadioDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>262</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="LtsDataView::LtsDataView" name="dataView" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,159 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialogModel provices data for CombatNetRadioDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#include "avm_combat_net_radio_dialog_model.h"
#include <ParticipantManagement/ppm_translation.h>
#include <LtsSession/lts_session_data.h>
#include <AudioVideoManagement/avm_ftc.h>
#include "avm_cnr_json_helper.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
namespace AudioVideoManagement
{
CombatNetRadioDialogModel::CombatNetRadioDialogModel(int32_t exerciseId, QObject *parent)
: LtsAbstractTableModel<CnrConfig>(
{
tr("Channel Number"),
tr("Radio Circuit"),
tr("Code Word"),
tr("Party"),
tr("Unit"),
tr("Global")
}, parent)
, m_exerciseId(exerciseId)
, m_logger(getLogger("CombatNetRadioDialogModel"))
{
}
QVariant CombatNetRadioDialogModel::data(const QModelIndex &index, int role) const
{
if ((role != Qt::BackgroundRole &&role != Qt::DisplayRole && role != Qt::UserRole) || !indexIsValid(index))
return {};
const CnrConfig &config = m_vecData.at(index.row());
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case CHANNEL:
INFO_LOG(m_logger)<<"CombatNetRadioDialogModel::data role =" << role
<< " row = " << index.row();
return QString::fromStdString(m_cnrChannels.value(config.cnrChannelId()).name());
case RADIO_CIRCUIT:
return QString::fromStdString(config.radioCircuit());
case CALL_SIGN:
return QString::fromStdString(config.callSign());
case PARTY:
if(config.exerciseId().is_set() || config.partyId() > 0)
return ParticipantManagement::trPartyName(m_parties.value(config.partyId()).name());
else
return QStringLiteral("-");
case UNIT:
{
if (!config.unitId().is_set())
return {};
const int unitId = config.unitId().get();
const auto unit = m_units.value(config.unitId().get());
if (unit.id() != unitId)
return {};
INFO_LOG(m_logger) << " config.unitId()=" << unitId
<< " Str=" << unit.name();
QString unitStr;
if (unit.designator().size() > 0)
unitStr = QString::fromStdString(unit.designator()) + QLatin1Char('/');
unitStr.append(QString::fromStdString(unit.tacticalIdentifier()));
INFO_LOG(m_logger) << "unitStr = " << unitStr;
return unitStr;
}
case GLOBAL:
return config.exerciseId().is_set() ? tr("No") : tr("Yes");
}
}
else if (role == Qt::BackgroundRole)
{
if (config.exerciseId().is_set() && config.exerciseId().get() == m_exerciseId)
return {};
if (!config.exerciseId().is_set() && m_exerciseId <= 0)
return {};
return QColor(Qt::gray);
}
else if (role == Qt::UserRole)
return QVariant::fromValue(config);
return {};
}
bool CombatNetRadioDialogModel::init(const std::shared_ptr<Interface::Dtc> &avmDtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc)
{
m_avmDtc = avmDtc;
m_cnrChannels = avmDtc->cnrChannels();
if (!ppmDtc->visibleParties(m_parties, LtsSessionData::LtsSessionData::getInstance()->getVisibleParties(m_exerciseId)))
{
ERROR_LOG(m_logger) << "Could not retrieve visible Parties!";
return false;
}
m_units = ppmDtc->units();
connect(m_avmDtc.get(), &Interface::Dtc::cnrConfigChanged, this, &CombatNetRadioDialogModel::onDataChanged);
m_avmDtc->cnrConfigs([this](const CnrConfig &c) { insertRow(c); });
return true;
}
bool CombatNetRadioDialogModel::onlyGlobals()
{
for(const CnrConfig &config : qAsConst(m_vecData))
{
if(config.exerciseId().is_set() && config.exerciseId().get() == m_exerciseId)
return false;
}
return true;
}
QByteArray CombatNetRadioDialogFilter::modelToJson() const
{
QVector<CnrConfig> configs;
configs.reserve(rowCount());
for (int row = 0; row < rowCount(); ++row)
{
int sourceRow = mapToSource(index(row, 0)).row();
configs.append(m_sourceModel->getRowData(sourceRow));
}
return cnrConfigVectorToJson(configs, m_exerciseId);
}
CombatNetRadioDialogFilter::CombatNetRadioDialogFilter(int32_t exerciseId, CombatNetRadioDialogModel *sourceModel)
: m_exerciseId(exerciseId)
, m_sourceModel(sourceModel)
{
Q_ASSERT(sourceModel);
setSourceModel(sourceModel);
}
bool CombatNetRadioDialogFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
CnrConfig cnrConfig = m_sourceModel->getRowData(sourceRow);
if (cnrConfig.exerciseId().is_set() && cnrConfig.exerciseId().get() != m_exerciseId)
return false;
return LtsColumnFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
} // namespace AudioVideoManagement
@@ -0,0 +1,85 @@
/**
* @RDE_HEADER@
* The CombatNetRadioDialogModel provices data for CombatNetRadioDialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#pragma once
// Todo:
#include <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <memory>
#include <cstdint>
namespace AudioVideoManagement
{
class CombatNetRadioDialogModel final
: public LtsWidgets::LtsAbstractTableModel<CnrConfig>
{
Q_OBJECT
public:
enum Columns
{
CHANNEL,
RADIO_CIRCUIT,
CALL_SIGN,
PARTY,
UNIT,
GLOBAL
};
explicit CombatNetRadioDialogModel(int32_t exerciseId, QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
bool init(const std::shared_ptr<Interface::Dtc> &avmDtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc);
bool onlyGlobals();
private:
std::shared_ptr<Interface::Dtc> m_avmDtc;
int32_t m_exerciseId;
QMap<int32_t, CnrChannel> m_cnrChannels;
QMap<int32_t, ParticipantManagement::Party> m_parties;
QMap<int32_t, ParticipantManagement::Participant> m_units;
LOGGER_T m_logger;
};
/**
* Responsible for filtering the CnrConfigs in a way that only those get displayed
* that are either global or local in the given exercise.
*/
class CombatNetRadioDialogFilter : public LtsWidgets::LtsColumnFilterProxyModel
{
public:
explicit CombatNetRadioDialogFilter(int32_t exerciseId, CombatNetRadioDialogModel *sourceModel);
QByteArray modelToJson() const;
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
/** Don't call directly, use the constructor instead. */
using LtsWidgets::LtsColumnFilterProxyModel::setSourceModel;
int32_t m_exerciseId;
CombatNetRadioDialogModel *m_sourceModel;
};
} // namespace AudioVideoManagement
Q_DECLARE_METATYPE(AudioVideoManagement::CnrConfig)
Q_DECLARE_METATYPE(AudioVideoManagement::FrequencyPlanningData)
@@ -0,0 +1,612 @@
/**
* @RDE_HEADER@
* The EditCombatNetRadioDialog allows to set data for a CNR Config
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#include "avm_edit_combat_net_radio_dialog.h"
#include "ui_avm_edit_combat_net_radio_dialog.h"
#include <AudioVideoManagement/av_helper.h>
#include <AudioVideoManagement/Shared/project_settings.h>
#include <ParticipantManagement/ppm_translation.h>
#include <LtsSession/lts_session_data.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
#include <ExerciseManagement/exm_dtc.h>
#include <QJsonObject>
#include <QJsonDocument>
#include <QStandardItemModel>
#include <QTimer>
namespace AudioVideoManagement
{
namespace
{
CnrConfig toConfig(const ModifyCnrConfigRequest &modifyRequest)
{
return {modifyRequest.id(), modifyRequest.cnrChannelId(), modifyRequest.exerciseId(), modifyRequest.radioCircuit(),
modifyRequest.partyId(), modifyRequest.unitId(), modifyRequest.recording(), modifyRequest.callSign(), false};
}
const QString &NO_CHANNEL()
{
static const QString noChannel = QStringLiteral("---");
return noChannel;
}
} // anonymous namespace
EditCombatNetRadioDialog::EditCombatNetRadioDialog(int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent)
: EditCombatNetRadioDialog(boost::none, exerciseId, session, ppmDtc, avmDtc, avmFtc, exmDtc, parent)
{
m_ui->chkRecording->setChecked(true);
m_isGlobal = session->partitionType() == IDL::PartitionType::Global;
if(m_isGlobal)
setUiValuesForGlobal();
initCbChannelsForCreate();
setOnlineHelpFilename(QStringLiteral("Situation-Combat_Net_Radio_Configuration"));
}
EditCombatNetRadioDialog::EditCombatNetRadioDialog(const CnrConfig &cnrConfig,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent)
: EditCombatNetRadioDialog(cnrConfig.id(), exerciseId, session, ppmDtc, avmDtc, avmFtc, exmDtc, parent)
{
m_channelId = cnrConfig.cnrChannelId();
m_isGlobal = session->partitionType() == IDL::PartitionType::Global;
setUiValues(cnrConfig);
setOnlineHelpFilename(QStringLiteral("Situation-Combat_Net_Radio_Configuration"));
}
EditCombatNetRadioDialog::EditCombatNetRadioDialog(const boost::optional<int32_t> &id,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ui(new Ui::EditCombatNetRadioDialog)
, m_id(id)
, m_exerciseId(exerciseId)
, m_ppmDtc(ppmDtc)
, m_avmDtc(avmDtc)
, m_avmFtc(avmFtc)
, m_exmDtc(exmDtc)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_logger(getLogger("EditCombatNetRadioDialog"))
{
Q_ASSERT(ppmDtc);
Q_ASSERT(avmDtc);
Q_ASSERT(avmFtc);
Q_ASSERT(exmDtc);
m_ui->setupUi(this);
if (ProjectSettings::isAlwaysRecording())
{
m_ui->chkRecording->setChecked(true);
m_ui->chkRecording->setEnabled(false);
m_ui->chkRecording->hide();
m_ui->lbRecording->hide();
}
init(session);
}
EditCombatNetRadioDialog::~EditCombatNetRadioDialog() = default;
void EditCombatNetRadioDialog::retranslate()
{
translate(m_ui.get(), QString(), !m_id.is_initialized(), tr("Combat Net Radio"));
}
void EditCombatNetRadioDialog::setUiValues(const CnrConfig &cnrConfig)
{
if(m_isGlobal)
setUiValuesForGlobal();
m_ui->chkGlobal->setChecked(m_isGlobal);
initCbChannels(cnrConfig.cnrChannelId());
bool disableParty = (m_avmDtc->cnrChannel(cnrConfig.cnrChannelId()).frequencyBand() == FrequencyBand_def::REL) || m_isGlobal;
if (!disableParty)
{
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(cnrConfig.partyId()));
int unitId = cnrConfig.unitId().is_set() ? cnrConfig.unitId().get() : 0;
m_ui->cbUnit->setCurrentIndex(m_ui->cbUnit->findData(unitId));
}
m_ui->leCallSign->setText(QString::fromStdString(cnrConfig.callSign()));
m_ui->chkRecording->setChecked(cnrConfig.recording());
m_ui->leRadioCircuit->setText(QString::fromStdString(cnrConfig.radioCircuit()));
onPartyIndexChanged(cnrConfig.partyId());
}
void EditCombatNetRadioDialog::setUiValuesForGlobal()
{
m_ui->chkGlobal->setChecked(true);
m_ui->cbUnit->setEnabled(false);
m_ui->lbUnit->setEnabled(false);
// m_ui->cbParty->addItem(QStringLiteral("-"));
// m_ui->cbParty->setCurrentText(QStringLiteral("-"));
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(1)); // Fixed on O/C as Ulli requested...
m_ui->cbParty->setEnabled(false);
m_ui->lbParty->setEnabled(false);
}
ModifyCnrConfigRequest EditCombatNetRadioDialog::createRequest()
{
ModifyCnrConfigRequest request;
request.id(m_id.get_value_or(0));
request.callSign(m_ui->leCallSign->text().toStdString());
request.cnrChannelId(m_ui->cbChannel->currentData().toInt());
request.radioCircuit(m_ui->leRadioCircuit->text().toStdString());
request.recording(m_ui->chkRecording->isChecked());
request.partyId(m_ui->cbParty->currentData().toInt());
if (!m_isGlobal)
request.exerciseId(m_exerciseId);
if (m_ui->cbUnit->currentIndex() > 0)
request.unitId().set(m_ui->cbUnit->currentIndex());
return request;
}
bool EditCombatNetRadioDialog::apply()
{
ModifyCnrConfigRequest request = createRequest();
if (m_ui->cbUnit->currentIndex() > 0)
{
request.unitId().set(m_ui->cbUnit->currentData().toInt());
INFO_LOG(m_logger)<< "currentIndex="<<m_ui->cbUnit->currentIndex()
<< " unitId ="<<request.unitId().get();
}
return m_requestHelper->startSingleRequest([this, request]()
{
return m_avmFtc->modifyCnrConfigRequest(request, this);
});
}
void EditCombatNetRadioDialog::init(LtsSession::LtsSession *session)
{
m_units = m_ppmDtc->units();
QMap<qint32, ParticipantManagement::Party> parties;
if (!m_ppmDtc->visibleParties(parties, LtsSessionData::LtsSessionData::getInstance()->getVisibleParties(m_exerciseId)))
ERROR_LOG(m_logger) << "Could not retrieve visible Parties!";
for (const ParticipantManagement::Party &party : qAsConst(parties))
m_ui->cbParty->addItem(ParticipantManagement::trPartyName(party.name()), party.id());
connect(m_ui->cbParty, QOverload<int>::of(&LtsWidgets::LtsComboBox::currentIndexChanged), this, &EditCombatNetRadioDialog::onPartyIndexChanged);
connect(m_ui->cbChannel, QOverload<int>::of(&LtsWidgets::LtsComboBox::currentIndexChanged), this, &EditCombatNetRadioDialog::onChannelChanged);
connect(m_avmDtc.get(), &Interface::Dtc::cnrConfigChanged, this, &EditCombatNetRadioDialog::updateFreeChannelsAndTakenNames);
connect(m_exmDtc, &ExerciseManagement::Interface::Dtc::exerciseChanged, this, &EditCombatNetRadioDialog::updateFreeChannelsAndTakenNames);
onPartyIndexChanged(m_ui->cbParty->currentIndex());
// Save/Load from Library Button
m_buttonBox->connectLibraryButton(this,
&EditCombatNetRadioDialog::onImportButtonClicked,
&EditCombatNetRadioDialog::onExportButtonClicked);
if (!session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui))
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Library, false);
m_uiRequirements->addRequirement({m_ui->lbRadioCircuit, [this] () { return isNameValid(m_ui->leRadioCircuit->text()); }, tr("name invalid")});
m_uiRequirements->addRequirement({m_ui->lbChannel, [this] () { return m_ui->cbChannel->currentData().toInt() > 0; }, tr("channel invalid")});
}
void EditCombatNetRadioDialog::initCbChannelsForCreate()
{
Q_ASSERT(!m_id.is_initialized());
Q_ASSERT(m_ui->cbChannel->count() == 0);
updateFreeChannelsAndTakenNames();
if (m_ui->cbChannel->count())
m_ui->cbChannel->setCurrentIndex(0); // select the first free channel
else
m_ui->cbChannel->addItem(NO_CHANNEL(), -1);
}
void EditCombatNetRadioDialog::initCbChannels(int channelId)
{
m_ui->cbChannel->clear();
updateFreeChannelsAndTakenNames();
m_ui->cbChannel->setCurrentIndex(m_ui->cbChannel->findData(channelId));
}
/**
* Called when one of the following changed:
* - Some Exercise
* - Some CnrConfig
*/
void EditCombatNetRadioDialog::updateFreeChannelsAndTakenNames()
{
const auto exercisesConsider = exercisesToConsider();
const auto exercisesIgnore = exercisesToIgnore();
m_takenNames.clear();
QVector<int> freeChannels = m_avmDtc->cnrChannels().keys().toVector();
QMap<int, QString> channelsToDisable; //<cnrChannelId, disable reason>
QMap<int, QString> exerciseNameCache;
const auto allCnrConfigs = m_avmDtc->cnrConfigs();
const ExerciseManagement::Exercise ownExercise = m_exmDtc->exercise(m_exerciseId);
ExerciseManagement::ExerciseState ownState = m_exmDtc->exercise(m_exerciseId).state();
for (const AudioVideoManagement::CnrConfig &cnrConfig : allCnrConfigs)
{
if (m_id.is_initialized() && *m_id == cnrConfig.id())
continue; // consider the channel of the CnrConfig we are editing right now as free
if (ownExercise.state() == ExerciseManagement::ExerciseState::Created && cnrConfig.exerciseId() != m_exerciseId)
{
if (ownExercise.linkedExerciseId() < 1 || cnrConfig.exerciseId() != ownExercise.linkedExerciseId())
continue;
if (!exerciseNameCache.contains(ownExercise.linkedExerciseId()))
exerciseNameCache.insert(ownExercise.linkedExerciseId(), QString::fromStdString(m_exmDtc->exercise(ownExercise.linkedExerciseId()).name()));
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in %1)").arg(exerciseNameCache.value(ownExercise.linkedExerciseId())));
continue;
}
if (ownExercise.state() == ExerciseManagement::ExerciseState::Started || ownExercise.state() == ExerciseManagement::ExerciseState::Paused)
{
if (cnrConfig.exerciseId().is_set())
{
int cnrExerciseId = cnrConfig.exerciseId().get();
if (exercisesConsider.contains(cnrExerciseId) && cnrExerciseId != m_exerciseId)
{
if (!exerciseNameCache.contains(cnrExerciseId))
exerciseNameCache.insert(cnrExerciseId, QString::fromStdString(m_exmDtc->exercise(cnrExerciseId).name()));
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in %1)").arg(exerciseNameCache.value(cnrExerciseId)));
continue;
}
}
}
if (!m_isGlobal)
{
if (cnrConfig.exerciseId().is_set() && !exercisesConsider.contains(cnrConfig.exerciseId().get()))
continue;
if (!cnrConfig.exerciseId().is_set() && ownState != ExerciseManagement::ExerciseState::Created)
{
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in global exercise)"));
continue;
}
}
if (m_isGlobal && cnrConfig.exerciseId().is_set() && exercisesConsider.contains(cnrConfig.exerciseId().get()))
{
int exerciseId = cnrConfig.exerciseId().get();
if (!exerciseNameCache.contains(exerciseId))
exerciseNameCache.insert(exerciseId, QString::fromStdString(m_exmDtc->exercise(exerciseId).name()));
channelsToDisable.insert(cnrConfig.cnrChannelId(), tr("(already taken in %1)").arg(exerciseNameCache.value(exerciseId)));
continue;
}
if (cnrConfig.exerciseId().is_set() && exercisesIgnore.contains(cnrConfig.exerciseId().get()))
continue;
m_takenNames.push_back(QString::fromStdString(cnrConfig.radioCircuit()));
freeChannels.removeOne(cnrConfig.cnrChannelId());
}
if (m_id.is_initialized())
{
if (freeChannels.contains(*m_channelId))
updateCbChannels({*m_channelId}, {});
else
updateCbChannels({},{});
}
else
updateCbChannels(std::move(freeChannels), std::move(channelsToDisable));
}
/**
* Update the channel-Combobox with the given freeChannels and keep the currently
* selected channel if possible.
*/
void EditCombatNetRadioDialog::updateCbChannels(QVector<int> &&freeChannels, QMap<int, QString> &&channelsToDisable)
{
const auto allChannels = m_avmDtc->cnrChannels();
const auto byName = [&allChannels] (int leftChannelId, int rightChannelId) { return QLatin1String(allChannels[leftChannelId].name().c_str()) < QLatin1String(allChannels[rightChannelId].name().c_str()); };
std::sort(freeChannels.begin(), freeChannels.end(), byName);
bool bOk;
int currentlySelectedChannelId = m_ui->cbChannel->currentData().toInt(&bOk); // -1 when no channel was selected
if (!bOk)
{
currentlySelectedChannelId = -1;
if (freeChannels.count() && channelsToDisable.contains(freeChannels.at(0)))
{
currentlySelectedChannelId = freeChannels.at(0);
while (channelsToDisable.contains(currentlySelectedChannelId))
{
int channelIndex = freeChannels.indexOf(currentlySelectedChannelId);
if (channelIndex == freeChannels.size() - 1)
{
currentlySelectedChannelId = -1;
break;
}
currentlySelectedChannelId = freeChannels.at(channelIndex + 1);
}
}
}
m_ui->cbChannel->clear();
for (const auto &channelId : freeChannels)
{
QString channelName = QString::fromStdString(allChannels[channelId].name());
if (channelsToDisable.contains(channelId))
{
QString text = QLatin1String("%1 %2").arg(channelName).arg(channelsToDisable.value(channelId));
m_ui->cbChannel->addItem(text, channelId);
auto * model = qobject_cast<QStandardItemModel*>(m_ui->cbChannel->model());
if(!model)
continue;
auto * item = model->item(m_ui->cbChannel->count() - 1);
if(!item)
continue;
item->setEnabled(false);
continue;
}
m_ui->cbChannel->addItem(channelName, channelId);
}
if (!m_ui->cbChannel->count())
m_ui->cbChannel->addItem(NO_CHANNEL(), -1);
else if (freeChannels.size() == 1)
m_ui->cbChannel->setCurrentIndex(m_ui->cbChannel->findData(currentlySelectedChannelId));
else if (currentlySelectedChannelId != -1)
QTimer::singleShot(100, this, [this, currentlySelectedChannelId] () { m_ui->cbChannel->setCurrentIndex(m_ui->cbChannel->findData(currentlySelectedChannelId)); });
}
/**
* If the current exercise is active or ex_0, return all active exercises.
* If the current exercise is planned but linked to another exercise, return the linked exercise.
* Otherwise, return only the current exercise.
*/
QVector<int> EditCombatNetRadioDialog::exercisesToConsider() const
{
auto isActive = [](ExerciseManagement::ExerciseState state) -> bool
{
return (state == ExerciseManagement::ExerciseState::Started ||
state == ExerciseManagement::ExerciseState::Paused);
};
const auto allExercises = m_exmDtc->exercises();
const ExerciseManagement::Exercise &currentExercise = allExercises.value(m_exerciseId);
QVector<int> toBeConsidered;
toBeConsidered.reserve(allExercises.size());
if (!m_isGlobal && currentExercise.state() == ExerciseManagement::ExerciseState::Created)
{
toBeConsidered.append(currentExercise.id());
if (currentExercise.linkedExerciseId() > 0)
toBeConsidered.append(currentExercise.linkedExerciseId());
return toBeConsidered;
}
if (!m_isGlobal && !isActive(allExercises[m_exerciseId].state()))
return {m_exerciseId};
for (const auto &exercise : allExercises)
{
if (isActive(exercise.state()))
toBeConsidered.append(exercise.id());
}
return toBeConsidered;
}
QVector<int> EditCombatNetRadioDialog::exercisesToIgnore() const
{
const auto allExercises = m_exmDtc->exercises();
auto canBeIgnored = [this] (const ExerciseManagement::Exercise &ex) -> bool
{
if (m_isGlobal)
{
return (ex.state() == ExerciseManagement::ExerciseState::Created ||
ex.state() == ExerciseManagement::ExerciseState::Stopped ||
ex.state() == ExerciseManagement::ExerciseState::Terminated ||
ex.isDeleted());
}
return (ex.state() == ExerciseManagement::ExerciseState::Stopped ||
ex.state() == ExerciseManagement::ExerciseState::Terminated ||
ex.isDeleted());
};
QVector<int> exercisesToIgnore;
for (const auto &ex : allExercises)
{
if (canBeIgnored(ex))
exercisesToIgnore += ex.id();
}
return exercisesToIgnore;
}
bool EditCombatNetRadioDialog::isNameValid(const QString &name) const
{
if (!isStreamNameValid(name))
return false;
if (m_takenNames.contains(name))
return false;
return true;
}
void EditCombatNetRadioDialog::onPartyIndexChanged(int index)
{
m_ui->cbUnit->clear();
m_ui->cbUnit->addItem(QLatin1String("-"), QVariant::fromValue(0));
int32_t partyId = m_ui->cbParty->itemData(index).toInt();
for (const ParticipantManagement::Participant &unit : qAsConst(m_units))
{
QString unitStr;
if (unit.partyId() == partyId)
{
if (unit.designator().size() > 0)
{
unitStr = QString::fromStdString(unit.designator()) + QLatin1Char('/');
INFO_LOG(m_logger)<<"Designator != leer: unitStr = " << unitStr;
}
unitStr += QString::fromStdString(unit.tacticalIdentifier());
INFO_LOG(m_logger) << "unitStr =" << unitStr
<< " id =" << unit.id();
m_ui->cbUnit->addItem(unitStr, unit.id());
}
}
}
void EditCombatNetRadioDialog::onChannelChanged(int index)
{
int channelId = m_ui->cbChannel->itemData(index).toInt();
bool disableParty = (m_avmDtc->cnrChannel(channelId).frequencyBand() == FrequencyBand_def::REL) || m_isGlobal;
m_ui->cbUnit->setDisabled(disableParty);
m_ui->lbUnit->setDisabled(disableParty);
m_ui->cbParty->setDisabled(disableParty);
m_ui->lbParty->setDisabled(disableParty);
if(disableParty && !m_isGlobal)
{
m_ui->cbParty->addItem(QStringLiteral("-"));
m_ui->cbParty->setCurrentText(QStringLiteral("-"));
}
}
// REGG-4433: We need to revise in which way we store the CNR configs in the library.
QByteArray EditCombatNetRadioDialog::cnrConfigRequestToJson()
{
ModifyCnrConfigRequest req = createRequest();
QJsonObject obj;
obj[QStringLiteral("id")] = req.id();
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::AVMEditCombatNetRadio);
obj[QStringLiteral("callSign")] = QString::fromStdString(req.callSign());
obj[QStringLiteral("cnrChannelId")] = req.cnrChannelId();
obj[QStringLiteral("radioCircuit")] = QString::fromStdString(req.radioCircuit());
obj[QStringLiteral("recording")] = req.recording();
obj[QStringLiteral("partyId")] = req.partyId();
obj[QStringLiteral("isGlobal")] = !req.exerciseId().is_set();
if (req.unitId().is_set())
obj[QStringLiteral("unitId")] = req.unitId().get();
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
ModifyCnrConfigRequest EditCombatNetRadioDialog::jsonToModifyCnrConfig(const QByteArray &content)
{
ModifyCnrConfigRequest result;
const QJsonDocument doc(QJsonDocument::fromJson(content));
const QJsonObject obj(doc.object());
result.id(obj[QStringLiteral("id")].toInt()); //TODO Library: check id = 0?
result.callSign(obj[QStringLiteral("callSign")].toString().toStdString());
result.cnrChannelId(obj[QStringLiteral("cnrChannelId")].toInt());
result.radioCircuit(obj[QStringLiteral("radioCircuit")].toString().toStdString());
result.recording(obj[QStringLiteral("recording")].toInt());
result.partyId(obj[QStringLiteral("partyId")].toInt());
if (!obj.contains(QLatin1String("isGlobal")) || !obj[QLatin1String("isGlobal")].toBool())
result.exerciseId(m_exerciseId);
if (obj.contains(QStringLiteral("unitId")))
result.unitId(obj[QStringLiteral("unitId")].toInt());
return result;
}
void EditCombatNetRadioDialog::onExportButtonClicked()
{
QByteArray byteArray = cnrConfigRequestToJson();
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_exerciseId, byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void EditCombatNetRadioDialog::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << " onImportButtonClicked() ";
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_exerciseId, FileManagement::toString(FileManagement::XmlType::AVMEditCombatNetRadio));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMEditCombatNetRadio)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
ModifyCnrConfigRequest req = jsonToModifyCnrConfig(content);
setUiValues(toConfig(req));
INFO_LOG(m_logger) << "--- onImportButtonClicked() ";
}
}
}
} // namespace AudioVideoManagement
@@ -0,0 +1,131 @@
/**
* @RDE_HEADER@
* The EditCombatNetRadioDialog allows to set data for a CNR Config
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2017-08-18
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsSession/lts_session.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <QVector>
#include <QString>
#include <boost/optional.hpp>
#include <memory>
class QCheckBox;
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace Ui
{
class EditCombatNetRadioDialog;
}
namespace AudioVideoManagement
{
class EditCombatNetRadioDialog
: public LtsWidgets::LtsDialog
, protected Interface::Ftc::ModifyCnrConfigReplyHandler
{
Q_OBJECT
public:
/**
* Used to create a new CnrConfig.
*/
explicit EditCombatNetRadioDialog(int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent);
/**
* Used for modifying an existing CnrConfig.
*/
explicit EditCombatNetRadioDialog(const CnrConfig &cnrConfig,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent);
~EditCombatNetRadioDialog();
void retranslate() override;
void onModifyCnrConfigReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
protected:
bool apply() override;
private: // methods
EditCombatNetRadioDialog(const boost::optional<int32_t> &id,
int32_t exerciseId,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<Interface::Ftc> &avmFtc,
ExerciseManagement::Interface::Dtc *exmDtc,
QWidget *parent);
void init(LtsSession::LtsSession *session);
void initCbChannelsForCreate();
void initCbChannels(int channelId);
void updateFreeChannelsAndTakenNames();
void updateCbChannels(QVector<int> &&freeChannels, QMap<int, QString> &&channelsToDisable);
QVector<int> exercisesToConsider() const;
QVector<int> exercisesToIgnore() const;
bool isNameValid(const QString &name) const;
QByteArray cnrConfigRequestToJson();
ModifyCnrConfigRequest createRequest();
ModifyCnrConfigRequest jsonToModifyCnrConfig(const QByteArray &content);
void setUiValues(const CnrConfig &cnrConfig);
void setUiValuesForGlobal();
private Q_SLOTS:
void onPartyIndexChanged(int index);
void onExportButtonClicked();
void onImportButtonClicked();
void onChannelChanged(int index);
private: // attributes
std::unique_ptr<Ui::EditCombatNetRadioDialog> m_ui;
boost::optional<int32_t> m_id; //!< only set when editing
int32_t m_exerciseId;
boost::optional<int32_t> m_channelId; //!< only set when editing
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<Interface::Dtc> m_avmDtc;
std::shared_ptr<Interface::Ftc> m_avmFtc;
ExerciseManagement::Interface::Dtc *m_exmDtc;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
QMap<int32_t, ParticipantManagement::Participant> m_units;
QVector<QString> m_takenNames;
std::unique_ptr<LtsUtils::RequestHelper> m_requestHelper;
bool m_isGlobal;
LOGGER_T m_logger;
}; // class EditCombatNetRadioDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditCombatNetRadioDialog</class>
<widget class="QWidget" name="EditCombatNetRadioDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>459</width>
<height>275</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Asset Stocks</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="lbChannel">
<property name="text">
<string>Channel Number:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LtsWidgets::LtsComboBox" name="cbChannel"/>
</item>
<item row="0" column="2" rowspan="4">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="lbRecording">
<property name="text">
<string>Recording:</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QCheckBox" name="chkRecording">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbRadioCircuit">
<property name="text">
<string>Radio Circuit:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="leRadioCircuit">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Radio Circuit</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="lbParty">
<property name="text">
<string>Party:</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="LtsWidgets::LtsComboBox" name="cbParty">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbCallSign">
<property name="text">
<string>Code Word:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="leCallSign">
<property name="toolTip">
<string>Code Word</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="lbUnit">
<property name="text">
<string>Unit:</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="LtsWidgets::LtsComboBox" name="cbUnit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lbGlobal">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Global:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="chkGlobal">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cbChannel</tabstop>
<tabstop>leRadioCircuit</tabstop>
<tabstop>leCallSign</tabstop>
<tabstop>cbParty</tabstop>
<tabstop>cbUnit</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,82 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialog allows to solve config for loaded CNR Configs
* $Id: avm_edit_combat_net_radio_dialog.h 60982 2023-12-01 10:18:25Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2024-01-30
*/
#include "avm_load_combat_net_radio_dialog.h"
#include "ui_avm_load_combat_net_radio_dialog.h"
#include "avm_cnr_json_helper.h"
#include "avm_load_combat_net_radio_dialog_model.h"
namespace AudioVideoManagement
{
LoadCombatNetRadioDialog::LoadCombatNetRadioDialog(const QByteArray &data,
LtsSession::LtsSession *session,
QWidget *parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_ui(new Ui::LoadCombatNetRadioDialog)
, m_model(new LoadCombatNetRadioDialogModel(data, session))
, m_logger(getLogger("LoadCombatNetRadioDialog"))
{
m_ui->setupUi(this);
m_ui->dataView->setModel(m_model);
m_ui->cbAll->setChecked(m_model->allChecked());
connect(m_ui->cbAll, &QCheckBox::clicked,
m_model, &LoadCombatNetRadioDialogModel::setCheckState);
connect(m_model, &LoadCombatNetRadioDialogModel::dataChanged,
this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
if (!roles.contains(Qt::CheckStateRole)
|| topLeft.column() != 0
|| bottomRight.column() != 0
|| m_ui->cbAll->isChecked() == m_model->allChecked())
return;
const QSignalBlocker blocker(m_ui->cbAll);
Q_UNUSED(blocker);
m_ui->cbAll->setChecked(!m_ui->cbAll->isChecked());
});
connect(m_ui->pbOk, &QPushButton::clicked,
this, &LoadCombatNetRadioDialog::onOk);
connect(m_ui->pbCancel, &QPushButton::clicked,
this, &LoadCombatNetRadioDialog::close);
}
QVector<Core::RemoveElementRequest> LoadCombatNetRadioDialog::removeRequests() const
{
if (m_isApplied)
return m_model->removeRequests();
return {};
}
QVector<ModifyCnrConfigRequest> LoadCombatNetRadioDialog::modifyRequests() const
{
if (m_isApplied)
return m_model->modifyRequests();
return {};
}
bool LoadCombatNetRadioDialog::isApplied() const
{
return m_isApplied;
}
void LoadCombatNetRadioDialog::onOk()
{
m_isApplied = true;
close();
}
LoadCombatNetRadioDialog::~LoadCombatNetRadioDialog() = default;
} // namespace AudioVideoManagement
@@ -0,0 +1,72 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialog allows to solve config for loaded CNR Configs
* $Id: avm_edit_combat_net_radio_dialog.h 60982 2023-12-01 10:18:25Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_edit_combat_net_radio_dialog.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2024-01-30
*/
#pragma once
#include <LtsWidgets/lts_frameless_dialog.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
namespace Ui
{
class LoadCombatNetRadioDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class LoadCombatNetRadioDialogModel;
class LoadCombatNetRadioDialog
: public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
explicit LoadCombatNetRadioDialog(const QByteArray &data,
LtsSession::LtsSession *session,
QWidget *parent = nullptr);
~LoadCombatNetRadioDialog();
QVector<Core::RemoveElementRequest> removeRequests() const;
QVector<ModifyCnrConfigRequest> modifyRequests() const;
bool isApplied() const;
private Q_SLOTS:
void onOk();
private: // attributes
std::unique_ptr<Ui::LoadCombatNetRadioDialog> m_ui;
LoadCombatNetRadioDialogModel *m_model;
bool m_isApplied = false;
LOGGER_T m_logger;
}; // class LoadCombatNetRadioDialog
} // namespace AudioVideoManagement
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoadCombatNetRadioDialog</class>
<widget class="QWidget" name="LoadCombatNetRadioDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>922</width>
<height>376</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Load Cnr Configs</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="cbAll">
<property name="text">
<string>Check/Uncheck all</string>
</property>
</widget>
</item>
<item>
<widget class="LtsDataView::LtsDataView" name="dataView" native="true"/>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pbOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,434 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialogModel provices data for LoadCombatNetRadioDialog
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#include "avm_load_combat_net_radio_dialog_model.h"
#include <ParticipantManagement/ppm_translation.h>
#include <LtsSession/lts_session_data.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <Core/core.hpp>
#include <FileManagement/flm_translation.h>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include "avm_cnr_json_helper.h"
#include <LtsSession/lts_session.h>
#include <ExerciseManagement/exm_data_plugin_interface.h>
#include <AudioVideoManagement/avm_data_plugin_interface.h>
namespace AudioVideoManagement
{
LoadCombatNetRadioDialogModel::LoadCombatNetRadioDialogModel(const QByteArray &data,
LtsSession::LtsSession *session,
QObject *parent)
: LtsAbstractTableModel<CnrConfig>(
{
tr("Include"),
tr("Channel"),
tr("Radio Circuit"),
tr("Code Word"),
tr("Conflict(s)")
}, parent)
, m_exerciseId(session == nullptr ? 0 : session->exerciseId())
, m_logger(getLogger("LoadCombatNetRadioDialogModel"))
{
DEBUG_LOG(m_logger) << "Own exercise id: " << m_exerciseId;
if (session != nullptr)
{
std::shared_ptr<AudioVideoManagement::Interface::Dtc> avmDtc;
if (!session->getDtc<AudioVideoManagement::DataInterface>(avmDtc)
|| !avmDtc->enableCnrChannel()
|| !avmDtc->enableCnrConfig())
ERROR_LOG(m_logger) << "Could not get AVM data";
else
{
m_cnrChannels = avmDtc->cnrChannels();
m_cnrConfigs = avmDtc->cnrConfigs();
}
DEBUG_LOG(m_logger) << "cnrconfigs: " << m_cnrConfigs.size();
DEBUG_LOG(m_logger) << "ownExerciseId: " << m_exerciseId;
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
if (!session->getDtc<ExerciseManagement::DataInterface>(exmDtc)
|| !exmDtc->enableExercise())
ERROR_LOG(m_logger) << "Could not get EXM data";
else
m_exercises = exmDtc->exercises();
DEBUG_LOG(m_logger) << "exercises: " << m_exercises.size();
}
else
ERROR_LOG(m_logger) << "Got invalid session.";
m_vecData = jsonToCnrConfigVector(data);
m_includeStates.reserve(m_vecData.size());
m_conflicts.reserve(m_vecData.size());
for (int32_t idxCounter = 0; idxCounter < m_vecData.size(); ++idxCounter)
{
Conflicts conflicts = getConflicts(idxCounter, m_vecData.at(idxCounter));
m_conflicts.append(conflicts);
m_includeStates.append(conflicts == NO_CONFLICT ? Qt::Checked : Qt::Unchecked);
if (conflicts & CHANNEL_TAKEN_IN_GLOBAL || conflicts & RADIO_CIRCUIT_TAKEN_IN_GLOBAL) // these conflicts must not be solved, so we disable the rows
m_disabledRows.append(idxCounter);
}
}
Qt::ItemFlags LoadCombatNetRadioDialogModel::flags(const QModelIndex &index) const
{
if (indexIsValid(index) && m_disabledRows.contains(index.row()))
return Qt::NoItemFlags;
if (index.column() > 0)
return LtsAbstractTableModel::flags(index);
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | LtsAbstractTableModel::flags(index);
}
bool LoadCombatNetRadioDialogModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (indexIsValid(index)
&& role == Qt::CheckStateRole
&& index.column() == 0
&& value.canConvert<Qt::CheckState>())
{
m_includeStates[index.row()] = value.value<Qt::CheckState>();
emit dataChanged(index, index, {role});
return true;
}
return QAbstractTableModel::setData(index, value, role);
}
QVariant LoadCombatNetRadioDialogModel::data(const QModelIndex &index, int role) const
{
if (!indexIsValid(index))
return QVariant();
switch (role)
{
case Qt::DisplayRole:
{
const CnrConfig &config = m_vecData.at(index.row());
switch (index.column())
{
case CHANNEL:
return QString::fromStdString(m_cnrChannels.value(config.cnrChannelId()).name());
case RADIO_CIRCUIT:
return QString::fromStdString(config.radioCircuit());
case CALL_SIGN:
return QString::fromStdString(config.callSign());
case CONFLICTS:
return conflictsText(m_conflicts.at(index.row()));
default:
return QVariant();
}
}
case Qt::CheckStateRole:
if (index.column() != 0)
return QVariant();
return m_includeStates.at(index.row());
case Qt::BackgroundRole:
return getConflictsRowColor(m_conflicts.at(index.row()));
default:
return QVariant();
}
}
bool LoadCombatNetRadioDialogModel::allChecked() const
{
for (int32_t row = 0; row < m_includeStates.size(); ++row)
{
if (m_disabledRows.contains(row))
continue;
if (m_includeStates.at(row) == Qt::Unchecked)
return false;
}
return true;
}
void LoadCombatNetRadioDialogModel::setCheckState(bool isChecked)
{
const QVariant data = QVariant(isChecked ? Qt::Checked : Qt::Unchecked);
const int role = Qt::CheckStateRole;
for (int32_t row = 0; row < rowCount(); ++row)
{
if (m_disabledRows.contains(row))
continue;
setData(createIndex(row, 0), data, role);
}
}
LoadCombatNetRadioDialogModel::Conflicts LoadCombatNetRadioDialogModel::getConflicts(int32_t row, const CnrConfig &config)
{
Conflicts conflicts = NO_CONFLICT;
for (auto it = m_cnrConfigs.constBegin(); it != m_cnrConfigs.constEnd(); ++it)
{
if (!(conflicts & RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG) && isRadioCircuitTakenInOwnConfig(config, it.value()))
{
conflicts |= RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE) && isRadioCircuitTakenInAnotherExercise(config, it.value()))
{
conflicts |= RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_ACTIVE_EXERCISE) && isChannelTakenInActiveExercise(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_ACTIVE_EXERCISE;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_LINKED_EXERCISE) && isChannelTakenInLinkedExercise(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_LINKED_EXERCISE;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_OWN_CONFIG) && isChannelTakenInOwnConfig(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_OWN_CONFIG;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & CHANNEL_TAKEN_IN_GLOBAL) && isChannelTakenInGlobal(config, it.value()))
{
conflicts |= CHANNEL_TAKEN_IN_GLOBAL;
addConflictedCnr(row, it.value().id());
}
if (!(conflicts & RADIO_CIRCUIT_TAKEN_IN_GLOBAL) && isRadioCircuitTakenInGlobal(config, it.value()))
{
conflicts |= RADIO_CIRCUIT_TAKEN_IN_GLOBAL;
addConflictedCnr(row, it.value().id());
}
}
return conflicts;
}
QString LoadCombatNetRadioDialogModel::conflictsText(Conflicts conflicts) const
{
QStringList conflictTexts;
if (conflicts & RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG)
conflictTexts.append(tr("Radio Circuit is already taken in own config."));
if (conflicts & RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE)
conflictTexts.append(tr("Radio Circuit is already taken in another exercise."));
if (conflicts & CHANNEL_TAKEN_IN_ACTIVE_EXERCISE)
conflictTexts.append(tr("Channel already taken in active exercise."));
if (conflicts & CHANNEL_TAKEN_IN_LINKED_EXERCISE)
conflictTexts.append(tr("Channel already taken in linked exercise."));
if (conflicts & CHANNEL_TAKEN_IN_OWN_CONFIG)
conflictTexts.append(tr("Channel is already taken in own Cnr Config."));
if (conflicts & CHANNEL_TAKEN_IN_GLOBAL)
conflictTexts.append(tr("Channel already taken in global Cnr Config."));
if (conflicts & RADIO_CIRCUIT_TAKEN_IN_GLOBAL)
conflictTexts.append(tr("Radio Circuit already taken in global Cnr Config."));
return conflictTexts.isEmpty() ? QLatin1String("-") : conflictTexts.join(QLatin1Char('\n'));
}
void LoadCombatNetRadioDialogModel::addConflictedCnr(int32_t row, int32_t cnrConfigId)
{
m_conflictedCnrIds[row].append(cnrConfigId);
}
bool LoadCombatNetRadioDialogModel::isActiveExercise(int32_t exerciseId) const
{
if (exerciseId == 0) // the global exercise is always active
return true;
using namespace ExerciseManagement;
ExerciseState exerciseState = m_exercises.value(exerciseId).state();
return exerciseState == ExerciseState::Started || exerciseState == ExerciseState::Paused;
}
QColor LoadCombatNetRadioDialogModel::getConflictsRowColor(Conflicts conflicts) const
{
if (conflicts >= CHANNEL_TAKEN_IN_GLOBAL)
return QColor(Qt::red).lighter();
if (conflicts > NO_CONFLICT)
return QColor(Qt::yellow).lighter();
return QColor(Qt::green).lighter();
}
bool LoadCombatNetRadioDialogModel::isRadioCircuitTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (m_exerciseId == 0)
return !existingConfig.exerciseId().is_set() && existingConfig.radioCircuit() == loadConfig.radioCircuit();
if (!existingConfig.exerciseId().is_set()
|| existingConfig.exerciseId().get() != m_exerciseId
|| existingConfig.radioCircuit() != loadConfig.radioCircuit())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isRadioCircuitTakenInAnotherExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (!isActiveExercise(m_exerciseId)
|| !existingConfig.exerciseId().is_set()
|| !isActiveExercise(existingConfig.exerciseId().get())
|| existingConfig.exerciseId().get() == m_exerciseId
|| existingConfig.radioCircuit() != loadConfig.radioCircuit())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInActiveExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
int32_t exerciseId = existingConfig.exerciseId().is_set() ? existingConfig.exerciseId().get() : 0;
if (!isActiveExercise(m_exerciseId)
|| exerciseId == 0
|| exerciseId == m_exerciseId
|| !isActiveExercise(exerciseId)
|| existingConfig.cnrChannelId() != loadConfig.cnrChannelId()
|| !isActiveExercise(exerciseId))
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInLinkedExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (m_exerciseId == 0 || m_exercises.value(m_exerciseId).linkedExerciseId() == 0)
return false;
int32_t linkedExerciseId = m_exercises.value(m_exerciseId).linkedExerciseId();
int32_t exerciseId = existingConfig.exerciseId().is_set() ? existingConfig.exerciseId().get() : 0;
if (exerciseId != linkedExerciseId || existingConfig.cnrChannelId() != loadConfig.cnrChannelId())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
int32_t exerciseId = existingConfig.exerciseId().is_set() ? existingConfig.exerciseId().get() : 0;
return existingConfig.cnrChannelId() == loadConfig.cnrChannelId() && exerciseId == m_exerciseId;
}
bool LoadCombatNetRadioDialogModel::isChannelTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (!isActiveExercise(m_exerciseId)
|| m_exerciseId == 0
|| existingConfig.exerciseId().is_set()
|| existingConfig.cnrChannelId() != loadConfig.cnrChannelId())
return false;
return true;
}
bool LoadCombatNetRadioDialogModel::isRadioCircuitTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const
{
if (!isActiveExercise(m_exerciseId)
|| m_exerciseId == 0
|| existingConfig.exerciseId().is_set()
|| existingConfig.radioCircuit() != loadConfig.radioCircuit())
return false;
return true;
}
QVector<Core::RemoveElementRequest> LoadCombatNetRadioDialogModel::removeRequests() const
{
QVector<Core::RemoveElementRequest> removeRequestsVec;
for (int32_t idxCounter = 0; idxCounter < m_includeStates.size(); ++idxCounter)
{
if (m_includeStates.at(idxCounter) == Qt::Checked && m_conflicts.at(idxCounter) != NO_CONFLICT)
{
if (!m_conflictedCnrIds.contains(idxCounter))
{
WARNING_LOG(m_logger) << "Tried to get remove request for row " << idxCounter << " but it is not present.";
continue;
}
const QVector<int32_t> cnrConfigIds = m_conflictedCnrIds.value(idxCounter);
for (int32_t cnrConfigId : cnrConfigIds)
{
Core::RemoveElementRequest removeRequest(cnrConfigId);
if (!removeRequestsVec.contains(removeRequest))
removeRequestsVec.append(removeRequest);
}
}
}
return removeRequestsVec;
}
QVector<ModifyCnrConfigRequest> LoadCombatNetRadioDialogModel::modifyRequests() const
{
const dds::core::optional<int32_t> exerciseId = m_exerciseId == 0 ? dds::core::optional<int32_t>() : dds::core::optional<int32_t>(m_exerciseId);
const int32_t partyId = m_exerciseId == 0 ? 1 : 0;
QVector<ModifyCnrConfigRequest> modifyRequestsVec;
modifyRequestsVec.reserve(m_includeStates.size());
for (int32_t row = 0; row < m_includeStates.size(); ++row)
{
if (m_includeStates.at(row) != Qt::Checked)
continue;
const CnrConfig &cnrConfig = m_vecData.at(row);
modifyRequestsVec.append({0,
cnrConfig.cnrChannelId(),
exerciseId,
cnrConfig.radioCircuit(),
cnrConfig.recording(),
{},
partyId,
cnrConfig.callSign()});
}
return modifyRequestsVec;
}
} // namespace AudioVideoManagement
@@ -0,0 +1,119 @@
/**
* @RDE_HEADER@
* The LoadCombatNetRadioDialogModel provices data for LoadCombatNetRadioDialog
* $Id: avm_combat_net_radio_dialog_model.h 60985 2023-12-01 11:53:28Z eberlein_v $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/optfunk/Lts/Plugins/AudioVideoManagement/Gui/CombatNetRadio/CombatNetRadioConfiguration/avm_combat_net_radio_dialog_model.h $
* $Author: eberlein_v $
* Created by Hannes Fritsche, ESZS, 2023-01-30
*/
#pragma once
#include <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <ExerciseManagement/exm_dtc.h>
namespace Core
{
class RemoveElementRequest;
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
class LoadCombatNetRadioDialogModel final
: public LtsWidgets::LtsAbstractTableModel<CnrConfig>
{
Q_OBJECT
public:
enum Columns
{
INCLUDE,
CHANNEL,
RADIO_CIRCUIT,
CALL_SIGN,
CONFLICTS
};
enum Conflict
{
NO_CONFLICT = 0,
RADIO_CIRCUIT_TAKEN_IN_OWN_CONFIG = 0b00000001,
RADIO_CIRCUIT_TAKEN_IN_ANOTHER_EXERCISE = 0b00000010,
CHANNEL_TAKEN_IN_ACTIVE_EXERCISE = 0b00000100,
CHANNEL_TAKEN_IN_LINKED_EXERCISE = 0b00001000,
CHANNEL_TAKEN_IN_OWN_CONFIG = 0b00010000,
// not solvable
CHANNEL_TAKEN_IN_GLOBAL = 0b00100000,
RADIO_CIRCUIT_TAKEN_IN_GLOBAL = 0b01000000,
};
Q_DECLARE_FLAGS(Conflicts, Conflict)
explicit LoadCombatNetRadioDialogModel(const QByteArray &data,
LtsSession::LtsSession *session,
QObject *parent = nullptr);
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QVariant data(const QModelIndex &index, int role) const override;
bool allChecked() const;
QVector<Core::RemoveElementRequest> removeRequests() const;
QVector<AudioVideoManagement::ModifyCnrConfigRequest> modifyRequests() const;
public Q_SLOTS:
void setCheckState(bool isChecked);
private: // methods
LoadCombatNetRadioDialogModel::Conflicts getConflicts(int32_t row, const CnrConfig &config);
QString conflictsText(Conflicts conflicts) const;
bool isRadioCircuitTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isRadioCircuitTakenInAnotherExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInActiveExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInLinkedExercise(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInOwnConfig(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isChannelTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
bool isRadioCircuitTakenInGlobal(const CnrConfig &loadConfig, const CnrConfig &existingConfig) const;
void addConflictedCnr(int32_t row, int32_t cnrConfigId);
bool isActiveExercise(int32_t exerciseId) const;
QColor getConflictsRowColor(Conflicts conflicts) const;
private: // attributes
int32_t m_exerciseId;
QMap<int32_t, CnrChannel> m_cnrChannels;
QMap<int32_t, CnrConfig> m_cnrConfigs;
QMap<int32_t, ExerciseManagement::Exercise> m_exercises;
QMap<int32_t, QVector<int32_t> > m_conflictedCnrIds; // <row, QVector<CnrConfig::id>>
QVector<Qt::CheckState> m_includeStates;
QVector<int32_t> m_disabledRows;
QVector<Conflicts> m_conflicts;
LOGGER_T m_logger;
};
} // namespace AudioVideoManagement