Files
qt5-legatus-legacy/Lts/Plugins/TargetManagement/Gui/ShootbackTargetConfiguration/tgt_assignment_part_model.cpp
T
2026-08-13 18:33:10 +02:00

704 lines
23 KiB
C++
Executable File

/**
* @RDE_HEADER@
* AssignmentParticipantModel
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, NLE, 2020-06-29
*/
#include "tgt_assignment_part_model.h"
#include "tgt_common.h"
#include <LtsUtilsCore/variant_operators.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <ParticipantManagement/ppm_translation.h>
#include <QApplication> // pixelMetric
#include <QBuffer>
namespace TargetManagement
{
// some helper
bool isEqual(const ShootbackConfiguration &c1, const ShootbackConfiguration &c2)
{
return (
(c1.interval1() == c2.interval1()) &&
(c1.flash1() == c2.flash1()) &&
(c1.effect1() == c2.effect1()) &&
(c1.interval2() == c2.interval2()) &&
(c1.flash2() == c2.flash2()) &&
(c1.effect2() == c2.effect2())
);
}
bool isDefault(const ShootbackConfiguration &c) { return isEqual(c, ShootbackConfiguration()); }
bool isEqual(const TargetAreaAssignment &a1, const TargetAreaAssignment &a2, bool ignoringAreaIdAndParticipantId)
{
return (
(a1.id() == a2.id()) &&
(a1.name() == a2.name()) &&
(ignoringAreaIdAndParticipantId || (a1.areaId() == a2.areaId())) &&
(ignoringAreaIdAndParticipantId || (a1.targetParticipantId() == a2.targetParticipantId())) &&
(a1.isActive() == a2.isActive()) &&
isEqual(a1.configuration(), a2.configuration())
);
}
//---------------------------------
// AssignmentParticipantModel
//---------------------------------
AssignmentParticipantModel::AssignmentParticipantModel(QObject *parent)
: BaseClass(
{
tr("Id"),
tr("Assignment Name"),
tr("Assigned"), // isActive
// associated target:
tr("TargetId"),
tr("TargetName"),
// associated participant:
tr("Participant Id"),
tr("Equipmentnumber"),
tr("Participant"),
// associated Area
tr("AreaId"),
tr("AreaName"),
// configuration
tr("Interval_1"),
tr("Flash_1"),
tr("Effect_1"),
tr("Interval_2"),
tr("Flash_2"),
tr("Effect_2")
},
parent)
, m_logger(getLogger("AssignmentParticipantModel"))
{}
bool AssignmentParticipantModel::initModel(const std::shared_ptr<TargetManagement::Interface::Dtc> &tgtDtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ParticipantManagement::ForeignGuiComboboxInterface> &ppmGuiCbxIfc,
LtsSession::LtsSession *session)
{
m_tgt_dtc = tgtDtc;
m_session = session;
delete m_participantHandler;
m_participantHandler = new ParticipantHandler(this);
m_participantHandler->init(m_tgt_dtc, ppmDtc, ppmGuiCbxIfc /*, exmDtc*/); // exercises are no relavant yet
connect(m_participantHandler, &ParticipantHandler::signalParticipantNamesChanged,
this, &AssignmentParticipantModel::signalParticipantNamesChanged);
connect(m_participantHandler, &ParticipantHandler::signalParticipantChanged,
this, &AssignmentParticipantModel::update);
// insert Model
const QMap<qint32, TargetManagement::TargetAreaAssignment> targetAreaAssignments = m_tgt_dtc->targetAreaAssignments();
INFO_LOG(m_logger) << "initModel - #targetAreaAssignments.size(): " << targetAreaAssignments.size();
connect(m_tgt_dtc.get(), &Interface::Dtc::targetAreaAssignmentChanged,
this, &AssignmentParticipantModel::onTargetAreaAssignmentChanged);
{
auto cb = [this](const TargetManagement::TargetAreaAssignment & d) { onTargetAreaAssignmentChanged(d, DataModification::eAdded); };
m_tgt_dtc->targetAreaAssignments(cb);
}
INFO_LOG(m_logger) << "initModel - #targetAreaAssignments.size(): " << targetAreaAssignments.size() << " DONE";
// updateColumnFilter
connect(this, &AssignmentParticipantModel::rowsInserted, this, &AssignmentParticipantModel::updateColumnFilter);
connect(this, &AssignmentParticipantModel::rowsRemoved, this, &AssignmentParticipantModel::updateColumnFilter);
connect(this, &AssignmentParticipantModel::modelReset, this, &AssignmentParticipantModel::updateColumnFilter);
// in case of changing value like hitsToKill:
connect(this, &AssignmentParticipantModel::dataChanged, this, &AssignmentParticipantModel::updateColumnFilter);
updateColumnFilter();
return true;
}
QVariant AssignmentParticipantModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole &&
orientation == Qt::Horizontal)
{
// a few QSet<QString> ==>
const auto processString = [](const QSet<QString> &usedXYCont) -> QVariant
{
LtsWidgets::ComboboxFilterContent cfc;
cfc.data.reserve(usedXYCont.size());
LtsWidgets::ComboboxFilterData cfd;
for (const QString &value : usedXYCont)
{
cfd.data = value;
cfd.text = value;
cfc.data.push_back(cfd);
}
cfc.sortItems = true;
return cfc.toVariant();
};
// a few Set<int> ==>
const auto processBoolean = [this](const QSet<int> &usedXYCont) -> QVariant
{
LtsWidgets::ComboboxFilterContent cfc;
cfc.data.reserve(usedXYCont.size());
LtsWidgets::ComboboxFilterData cfd;
for (int value : usedXYCont)
{
cfd.data = value;
cfd.text = value ? tr("Yes") : tr("No");
cfd.icon = value ? m_yes : m_no;
cfc.data.push_back(cfd);
}
cfc.sortItems = true;
return cfc.toVariant();
};
// a few Set<int> ==>
const auto processInt = [](const QSet<int> &usedXYCont) -> QVariant
{
LtsWidgets::ComboboxFilterContent cfc;
cfc.data.reserve(usedXYCont.size());
LtsWidgets::ComboboxFilterData cfd;
for (int value : usedXYCont)
{
cfd.data = value;
cfd.text = QString::number(value);
cfc.data.push_back(cfd);
}
cfc.sortItems = true;
return cfc.toVariant();
};
if (section == IsActive)
return processBoolean(m_usedIsActive);
else if (section == AreaId)
return processInt(m_usedAreaIds);
else if (section == AreaName)
return processString(m_usedAreas);
else if (section == TargetId)
return processInt(m_usedTargetIds);
else if (section == TargetName)
return processString(m_usedTargets);
else if (section == TargetPartipantId)
return processInt(m_usedTargetParticipantIds);
else if (section == TargetPartipantEquipmentNumber)
return processInt(m_usedParticipantEqNo);
else if (section == Participant)
return processString(m_usedParticipants);
}
return BaseClass::headerData(section, orientation, role);
}
template <typename T>
QVariant handleIsDefault(const TargetManagement::TargetAreaAssignment &assignment, const T &value)
{
if (isDefault(assignment.configuration()))
return QStringLiteral("-");
return value;
}
QVariant AssignmentParticipantModel::data(const QModelIndex &index, int role) const
{
INFO_LOG(m_logger) << LOGFUNCTION << " role=" << role
<< " index = " << index.column();
if (!indexIsValid(index))
return QVariant();
if (role != Qt::DisplayRole &&
role != Qt::EditRole &&
role != Qt::CheckStateRole &&
role != Qt::DecorationRole &&
role != Qt::ForegroundRole &&
role != Qt::BackgroundRole &&
role != Qt::ToolTipRole &&
role != LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole)
return QVariant();
const TargetManagement::TargetAreaAssignment &assignment = m_vecData.at(index.row());
if (role == Qt::CheckStateRole)
{
// CheckBoxes only for columns that are actually checkable
if (!flags(index).testFlag(Qt::ItemIsUserCheckable))
return QVariant();
return assignment.isActive() ? Qt::Checked : Qt::Unchecked;
}
else if (role == Qt::DecorationRole)
{
if (index.column() == Participant)
{
static const int pmSize = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
const QPixmap &pm = m_participantHandler->getParticipantDecoration(assignment.targetParticipantId());
if (!pm.isNull())
return pm.scaled(pmSize, pmSize);
}
else if ((index.column() == Flash_1))
return assignment.configuration().flash1() ? m_yes : m_no;
else if ((index.column() == Flash_2))
return assignment.configuration().flash2() ? m_yes : m_no;
return QVariant();
}
else if (role == Qt::BackgroundRole)
{
if ((index.column() == TargetId) || (index.column() == TargetName))
{
int targetId = m_participantHandler->getParticipantIdToTargetId(assignment.targetParticipantId());
if (targetId <= 0)
return getNoParticipantColor(); // orange
}
else if ((index.column() == AreaId) || (index.column() == AreaName))
{
if (m_tgt_dtc->shootbackArea(assignment.areaId()).id()<=0)
return getNoParticipantColor(); // orange
}
else if ((index.column() == TargetPartipantId) && (assignment.targetParticipantId() <= 0))
return getNoParticipantColor(); // orange
else if (index.column() == TargetPartipantEquipmentNumber)
{
const int eqNo = m_participantHandler->getParticipantIdToEquipmentNumber(assignment.targetParticipantId());
if (eqNo <= 0)
return getNoParticipantColor(); // orange
}
// no else here
if (assignment.isActive() && isDefault(assignment.configuration()))
return getAssignedButNoConfigurationColor(); // lightRed
}
else if (role == Qt::ForegroundRole)
{
if (!assignment.isActive() && (index.column() != Participant))
return QColor(Qt::gray);
}
else if (role == Qt::ToolTipRole)
{
if (index.column() == Participant)
{
QString tooltip(QStringLiteral("<html>"));
const QPixmap &img = m_participantHandler->getParticipantDecoration(assignment.targetParticipantId());
if (!img.isNull())
{
// without a temporary file:
QByteArray data;
QBuffer buffer(&data);
img.save(&buffer, "PNG", 100);
tooltip.append(QStringLiteral("<center><img src='data:image/png;base64,%1'></center>").arg(QString::fromLatin1(data.toBase64())));
QString hdr = this->headerData(AssignmentName, Qt::Horizontal, Qt::DisplayRole).toString();
tooltip.append(QStringLiteral("<p>%1: %2</p>").arg(hdr, QString::fromStdString(assignment.name())));
hdr = this->headerData(Participant, Qt::Horizontal, Qt::DisplayRole).toString();
const QString &val = m_participantHandler->getParticipantHierarchie(assignment.targetParticipantId());
tooltip.append(QStringLiteral("<p>%1: %2</p>").arg(hdr, val));
tooltip.append(QStringLiteral("</html>"));
return tooltip;
}
}
}
else if (role == LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole)
{
// following static_cast<int> are important!!!
if (index.column() == IsActive)
return static_cast<int>(assignment.isActive());
if (index.column() == AreaId)
return assignment.areaId();
if (index.column() == TargetPartipantId)
return assignment.targetParticipantId();
}
switch (index.column())
{
case Id:
return assignment.id();
case AssignmentName:
return QString::fromStdString(assignment.name());
case IsActive:
return assignment.isActive() ? tr("Yes") : tr("No");
case TargetId:
{
int targetId = m_participantHandler->getParticipantIdToTargetId(assignment.targetParticipantId());
return targetId;
}
case TargetName:
{
int targetId = m_participantHandler->getParticipantIdToTargetId(assignment.targetParticipantId());
if (targetId <= 0)
return QStringLiteral("-");
const ShootbackTarget &st = m_tgt_dtc->shootbackTarget(targetId);
if (st.id() != targetId)
return QStringLiteral("-");
return QString::fromStdString(st.name());
}
// associated participant:
case TargetPartipantId:
return assignment.targetParticipantId();
case TargetPartipantEquipmentNumber:
{
const int eqNo = m_participantHandler->getParticipantIdToEquipmentNumber(assignment.targetParticipantId());
if (eqNo <= 0)
return QStringLiteral("-");
return eqNo;
}
case Participant:
return m_participantHandler->getParticipantHierarchie(assignment.targetParticipantId());
// associated area:
case AreaId:
return assignment.areaId();
case AreaName:
return QString::fromStdString(m_tgt_dtc->shootbackArea(assignment.areaId()).name());
// configuration:
case Interval_1:
return handleIsDefault(assignment, assignment.configuration().interval1());
case Flash_1:
return handleIsDefault(assignment, assignment.configuration().flash1());
case Effect_1:
return handleIsDefault(assignment, effectToString(assignment.configuration().effect1()));
case Interval_2:
return handleIsDefault(assignment, assignment.configuration().interval2());
case Flash_2:
return handleIsDefault(assignment, assignment.configuration().flash2());
case Effect_2:
return handleIsDefault(assignment, effectToString(assignment.configuration().effect2()));
} // switch
return QVariant();
}
bool AssignmentParticipantModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!indexIsValid(index))
return false;
if (role != Qt::CheckStateRole)
return false;
if (!value.canConvert<bool>())
return false;
bool bVal = value.toBool();
TargetAreaAssignment &data = m_vecData[index.row()];
if (data.isActive() != bVal)
{
data.isActive(bVal);
emit dataChanged(index, index);
emit signalEditableItemChanged();
}
return true;
}
Qt::ItemFlags AssignmentParticipantModel::flags(const QModelIndex &index) const
{
auto f = QAbstractTableModel::flags(index);
if (!index.isValid() || index.row() >= m_vecData.size())
return f;
if (index.column() == Columns::IsActive)
{
f.setFlag(Qt::ItemIsUserCheckable);
return f;
}
return f;
}
QSet<int> AssignmentParticipantModel::getAssociatedParticipantIds() const
{
QSet<int> ret;
ret.reserve(m_vecData.size());
for (const TargetManagement::TargetAreaAssignment &data : m_vecData)
ret += data.targetParticipantId();
return ret;
}
QSet<int> AssignmentParticipantModel::getAssociatedParticipantIdsForArea(int areaId) const
{
QSet<int> ret;
ret.reserve(m_vecData.size());
for (const TargetManagement::TargetAreaAssignment &data : m_vecData)
if (data.areaId() == areaId)
ret += data.targetParticipantId();
return ret;
}
QSet<int> AssignmentParticipantModel::getAssociatedAreasIdsForParticipantId(int participantId) const
{
QSet<int> ret;
ret.reserve(m_vecData.size());
for (const TargetManagement::TargetAreaAssignment &data : m_vecData)
if (data.targetParticipantId() == participantId)
ret += data.areaId();
return ret;
}
int AssignmentParticipantModel::findRowForAreaTargetParticipantId(int areaId, int targetParticipantId)
{
for (int i = 0; i < m_vecData.size(); ++i) // clazy:exclude=range-loop
{
const TargetAreaAssignment &curAssignment = m_vecData.at(i);
if ((curAssignment.areaId() == areaId) && (curAssignment.targetParticipantId() == targetParticipantId))
return i;
}
return -1;
}
void AssignmentParticipantModel::addGapFillerForArea(int areaId)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START, areaId=" << areaId;
const QSet<int> &associatedParticipantIds = getAssociatedParticipantIdsForArea(areaId);
QVector<TargetManagement::TargetAreaAssignment> gapFillers;
#define USE_PARTICIPANTS_OF_ORBAT
#ifdef USE_PARTICIPANTS_OF_ORBAT
// participantIds with EquipmentType (WmsTypeClass::TargetVehicle||WmsTypeClass::TargetHuman)
QList<int> pIds = getParticipantHierarchies().keys();
for (auto pId : qAsConst(pIds))
{
if (!associatedParticipantIds.contains(pId))
{
TargetManagement::TargetAreaAssignment gapFiller;
gapFiller.areaId(areaId);
gapFiller.targetParticipantId(pId);
gapFiller.isActive(false);
gapFillers.push_back(gapFiller);
}
} // for
#else // USE_PARTICIPANTS_OF_ORBAT
// use just the (base)configured targets:
auto cb = [associatedParticipantIds, areaId, &gapFillers](const TargetManagement::ShootbackTarget & targets)
{
if (targets.targetParticipantId() <= 0)
return;
if (!associatedParticipantIds.contains(targets.targetParticipantId()))
{
TargetManagement::TargetAreaAssignment gapFiller;
gapFiller.areaId(areaId);
gapFiller.targetParticipantId(targets.targetParticipantId());
gapFiller.isActive(false);
gapFillers.push_back(gapFiller);
}
};
m_tgt_dtc->shootbackTargets(cb);
#endif
this->insertRows(gapFillers);
DEBUG_LOG(m_logger) << LOGFUNCTION << " END, areaId=" << areaId;
}
void AssignmentParticipantModel::addGapFillerForTargetParticipantId(int targetParticipantId)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START, targetParticipantId=" << targetParticipantId;
const QSet<int> &associatedAreaIds = getAssociatedAreasIdsForParticipantId(targetParticipantId);
QVector<TargetManagement::TargetAreaAssignment> gapFillers;
// gapFillers.reserve(1000);
auto cb = [associatedAreaIds, targetParticipantId, &gapFillers](const TargetManagement::ShootbackArea & area)
{
if (!associatedAreaIds.contains(area.id()))
{
TargetManagement::TargetAreaAssignment gapFiller;
gapFiller.areaId(area.id());
gapFiller.targetParticipantId(targetParticipantId);
gapFiller.isActive(false);
gapFillers.push_back(gapFiller);
}
};
m_tgt_dtc->shootbackAreas(cb);
this->insertRows(gapFillers);
DEBUG_LOG(m_logger) << LOGFUNCTION << " END, targetParticipantId=" << targetParticipantId;
}
void AssignmentParticipantModel::onTargetAreaAssignmentChanged(const TargetManagement::TargetAreaAssignment &data,
DataModification modType)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " modType=" << modType << " data=" << data;
// avoid adding an assignment which already exists:
if (modType == DataModification::eAdded)
{
int row = findRowForAreaTargetParticipantId(data.areaId(), data.targetParticipantId());
DEBUG_LOG(m_logger) << LOGFUNCTION << " row=" << row;
if (row >= 0)
{
setRowData(row, data);
emit signalParticipantNamesChanged();
return;
}
}
BaseClass::onDataChanged(data, modType);
emit signalParticipantNamesChanged();
}
void AssignmentParticipantModel::updateColumnFilter()
{
m_usedIsActive.clear();
m_usedAreaIds.clear();
m_usedAreas.clear();
m_usedTargetIds.clear();
m_usedTargets.clear();
m_usedTargetParticipantIds.clear();
m_usedParticipantEqNo.clear();
m_usedParticipants.clear();
int row = -1;
for (const TargetAreaAssignment &assignment : qAsConst(m_vecData))
{
++row;
// This loops over ALL item, even if some of them are filtered by the AssignmentParticipantProxyModel (areaId, ..)
m_usedIsActive.insert(assignment.isActive());
m_usedAreaIds.insert(assignment.areaId());
m_usedAreas.insert( QString::fromStdString(m_tgt_dtc->shootbackArea(assignment.areaId()).name()) );
int targetId = m_participantHandler->getParticipantIdToTargetId(assignment.targetParticipantId());
m_usedTargetIds.insert(targetId);
m_usedTargets.insert(this->data(index(row, TargetName), Qt::DisplayRole).toString());
m_usedTargetParticipantIds.insert(assignment.targetParticipantId());
const int eqNo = m_participantHandler->getParticipantIdToEquipmentNumber(assignment.targetParticipantId());
m_usedParticipantEqNo.insert(eqNo);
m_usedParticipants.insert(m_participantHandler->getParticipantHierarchie(assignment.targetParticipantId()));
}
}
QString AssignmentParticipantModel::effectToString(const Effect &effect) const
{
switch (effect.underlying())
{
case TargetManagement::Effect_def::NearMiss:
return tr("NearMiss");
case TargetManagement::Effect_def::MobilityKill:
return tr("MobilityKill");
case TargetManagement::Effect_def::WeaponKill:
return tr("WeaponKill");
default:
{
// empty
}
}
return QStringLiteral("-");
}
// --------------------------------------------------------
// AssignmentParticipantProxyModel
// --------------------------------------------------------
void AssignmentParticipantProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<AssignmentParticipantModel *>(sourceModel);
BaseClass::setSourceModel(sourceModel);
}
void AssignmentParticipantProxyModel::filterToAreaId(int areaId)
{
m_areaId = areaId;
m_targetParticipantId = -1;
invalidateFilter();
}
void AssignmentParticipantProxyModel::filterToTargetParticpantId(int targetParticpantId)
{
m_targetParticipantId = targetParticpantId;
m_areaId = -1;
invalidateFilter();
}
bool AssignmentParticipantProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (!m_sourceModel)
return BaseClass::filterAcceptsRow(source_row, source_parent);
if (m_areaId > 0)
{
const TargetManagement::TargetAreaAssignment &item = m_sourceModel->m_vecData.at(source_row);
return (item.areaId() == m_areaId) && BaseClass::filterAcceptsRow(source_row, source_parent);
}
else if ((m_targetParticipantId > 0))
{
const TargetManagement::TargetAreaAssignment &item = m_sourceModel->m_vecData.at(source_row);
return (item.targetParticipantId() == m_targetParticipantId) && BaseClass::filterAcceptsRow(source_row, source_parent);;
}
return BaseClass::filterAcceptsRow(source_row, source_parent);
}
bool AssignmentParticipantProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel || !m_sourceModel->m_participantHandler ||
source_left.column() != source_right.column() ||
source_left.row() < 0 || source_left.row() >= m_sourceModel->m_vecData.size() ||
source_right.row() < 0 || source_right.row() >= m_sourceModel->m_vecData.size()
)
return BaseClass::lessThan(source_left, source_right);
const TargetManagement::TargetAreaAssignment &left = m_sourceModel->m_vecData.at(source_left.row());
const TargetManagement::TargetAreaAssignment &right = m_sourceModel->m_vecData.at(source_right.row());
switch (source_left.column())
{
case AssignmentParticipantModel::Columns::Id:
return left.id() < right.id();
case AssignmentParticipantModel::Columns::AssignmentName:
return left.name() < right.name();
case AssignmentParticipantModel::Columns::TargetPartipantId:
return left.targetParticipantId() < right.targetParticipantId();
case AssignmentParticipantModel::Columns::TargetPartipantEquipmentNumber:
{
const int eqNoLeft = m_sourceModel->m_participantHandler->getParticipantIdToEquipmentNumber(left.targetParticipantId());
const int eqNoRight = m_sourceModel->m_participantHandler->getParticipantIdToEquipmentNumber(right.targetParticipantId());
return eqNoLeft < eqNoRight;
}
case AssignmentParticipantModel::Columns::AreaId:
return left.areaId() < right.areaId();
case AssignmentParticipantModel::Columns::Participant:
case AssignmentParticipantModel::Columns::AreaName:
// QString compare
return LtsUtils::lessThan(source_left.data(), source_right.data());
}
return BaseClass::lessThan(source_left, source_right);
}
} // namespace TargetManagement