Files
qt5-legatus-legacy/Lts/Plugins/ParticipantManagement/Gui/ParticipantInfo/participant_info_table_model.cpp
T
2026-08-13 18:33:10 +02:00

417 lines
13 KiB
C++
Executable File

/**
* @RDE_HEADER@
* Model for TlnInfo Table
* $Id$
* $HeadURL$
* $Author$
* Created by Astrid Mueller, SGAS, 2017-07-10
*/
#include "participant_info_table_model.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/outdated_position.h>
#include <ProjectDataManagement/pdm_to_string.h>
#include <ParticipantManagement/logger.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <QSet>
#include <QTimer>
namespace ParticipantManagement
{
ParticipantInfoTableModel::ParticipantInfoTableModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<ParticipantInfo>({tr("Equip. No."),
tr("Tact. Status"),
tr("Designator"),
tr("Tact. Identifier"),
tr("Position"),
tr("BITE Result")},
parent)
, m_session(nullptr)
, m_ppmDtc(nullptr)
, m_ppmGuiCbxIfc(nullptr)
, m_partitionTime(0)
, m_timeoutCheckTimer(new QTimer(this))
, m_bWithPositionandBiteResult(true)
, m_logger(getLogger("ParticipantInfoTableModel"))
{
m_timeoutCheckTimer->setSingleShot(false);
connect(m_timeoutCheckTimer, &QTimer::timeout,
this, &ParticipantInfoTableModel::onTimeoutCheck);
}
ParticipantInfoTableModel::~ParticipantInfoTableModel()
{
}
bool ParticipantInfoTableModel::initModel(LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
bool bWithPositionandBiteResult)
{
DEBUG_LOG(m_logger) << "**** initModel";
m_session = session;
m_ppmDtc = ppmDtc;
session->getForeignGuiInterface<ParticipantManagement::ForeignGuiComboboxInterface>(m_ppmGuiCbxIfc);
m_bWithPositionandBiteResult = bWithPositionandBiteResult;
connect(m_ppmDtc.get(),
&ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged,
this, &ParticipantInfoTableModel::onParticipantChanged);
connect(m_ppmDtc.get(),
&ParticipantManagement::Interface::Dtc::partyChanged,
this, &ParticipantInfoTableModel::onPartyChanged);
if (m_bWithPositionandBiteResult)
{
connect(m_ppmDtc.get(),
&ParticipantManagement::Interface::Dtc::biteChanged,
this, &ParticipantInfoTableModel::onBiteChanged);
connect(m_ppmDtc.get(),
&ParticipantManagement::Interface::Dtc::participantPositionChanged,
this, &ParticipantInfoTableModel::onPositionChanged);
connect(m_session, &LtsSession::LtsSession::partitionTimeChanged,
this, &ParticipantInfoTableModel::onPartitionTimeChanged);
m_timeoutCheckTimer->start(2000);
}
auto cbParty = [this](const ParticipantManagement::Party &party)
{
onPartyChanged(party, DataModification::eAdded);
};
m_ppmDtc->partys(cbParty);
return true;
}
void ParticipantInfoTableModel::initializeDataView()
{
beginResetModel();
m_vecData.clear();
auto cb = [this](const Participant &p)
{
if (!p.equipmentNumber().is_set() || p.equipmentNumber().get() == 0)
return;
m_vecData += setInfoData(p);
};
m_ppmDtc->participants(cb);
endResetModel();
}
QVariant ParticipantInfoTableModel::data(const QModelIndex &index, int role) const
{
if (!indexIsValid(index))
return QVariant();
const ParticipantInfo &data = m_vecData.at(index.row());
if (role == Qt::BackgroundRole)
{
switch(index.column())
{
case EquipmentNumber:
if (data.isManipulated)
return QColor(Qt::red);
break;
case Position:
if (data.isOutdated)
return QColor(Qt::red);
break;
case BiteResult:
if (data.biteSampleTime < (m_session->partitionTime() - (2 * 60 *1000)) ) //<- so 2 minutes
return (QColor(Qt::darkGray));
break;
default:
break;
} // end switch
return QVariant();
}
if (role == Qt::ToolTipRole && index.column() == TacticalStatus)
return vulnerabilityStateString(data);
if (role == Qt::DecorationRole)
{
if (index.column() == TacticalStatus)
{
static const QIcon green(QLatin1String("icon:green_light.svg"));
static const QIcon yellow(QLatin1String("icon:yellow_light.svg"));
static const QIcon red(QLatin1String("icon:red_light.svg"));
switch (data.vulnerabilityState.underlying())
{
case ProjectDataManagement::VulnerabilityState::CombatReady:
return green;
case ProjectDataManagement::VulnerabilityState::CatastrophicKill:
return red;
default:
return yellow;
}
}
if (index.column() == EquipmentNumber)
return m_partyIcons.value(data.partyId);
return QVariant();
}
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case EquipmentNumber:
if (data.equipmentNumber > 0)
return data.equipmentNumber;
return QVariant();
case TacticalIdentifier:
return data.tacticalIdentifier;
case Designator:
return data.designator;
case Position:
return data.positionString;
case BiteResult:
return data.biteResult;
}
}
return QVariant();
}
bool ParticipantInfoTableModel::removeItems(const QVector<int> &selectedIds)
{
if (selectedIds.isEmpty())
return true;
for (int id : selectedIds)
removeRow(findRowForId(id));
return true;
}
void ParticipantInfoTableModel::updateDataView(const QSet<int> &participantIds)
{
if (participantIds.isEmpty())
return;
QSet<int> participantIdsToAdd(participantIds);
// remove all which are already in list
for (const auto &p : qAsConst(m_vecData))
participantIdsToAdd.remove(p.id());
if (participantIdsToAdd.isEmpty())
return;
std::function<bool(const ParticipantManagement::Participant &)> cb =
[this, &participantIdsToAdd](const ParticipantManagement::Participant &part)
{
auto it = participantIdsToAdd.find(part.id());
if (it != participantIdsToAdd.end())
{
participantIdsToAdd.erase(it);
insertRow(setInfoData(part));
}
// return true when no more ids to add to stop loop
return participantIdsToAdd.isEmpty();
};
m_ppmDtc->participants(cb);
}
void ParticipantInfoTableModel::onParticipantChanged(const ParticipantManagement::DynamicParticipantData &data,
DataModification modType)
{
DEBUG_LOG(m_logger) << "**** participantDataChanged";
const int row = findRowForId(data.participantId());
if (row < 0)
return;
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
{
ParticipantInfo info = getRowData(row);
if (info.participantId != data.participantId())
fillParticipantContent(info, data);
info.vulnerabilityState = m_ppmDtc->youngestStateInformation(data.stateHistory()).vulnerabilityState();
info.isManipulated = data.manipulation().size();
setRowData(row, info);
break;
}
case DataModification::eRemoved:
removeRow(row);
break;
}
}
ParticipantInfo ParticipantInfoTableModel::setInfoData(const Participant &part) const
{
ParticipantInfo data;
const int participantId = part.id();
fillParticipantContent(data, part);
if (m_bWithPositionandBiteResult)
{
ParticipantManagement::Bite participantBite;
std::function<bool(const ParticipantManagement::Bite &)> cbBite =
[&participantBite, participantId](const ParticipantManagement::Bite &bite)
{
if (participantId != bite.participantId())
return false;
participantBite = bite;
return true;
};
m_ppmDtc->bites(cbBite);
data.biteResult = biteStringFromBite(participantBite);
data.biteSampleTime = participantBite.sampleTime();
std::function<bool(const ParticipantManagement::ParticipantPosition &data)> cbPos =
[this, &data, participantId](const ParticipantManagement::ParticipantPosition &pos)
{
if (participantId != pos.participantId())
return false;
updatePosition(data, pos);
return true;
};
m_ppmDtc->participantPositions(cbPos);
}
const auto dynPart = m_ppmDtc->dynamicParticipantData(participantId);
data.vulnerabilityState = m_ppmDtc->youngestStateInformation(dynPart.stateHistory()).vulnerabilityState();
data.isManipulated = dynPart.manipulation().size() != 0;
data.designator = QString::fromStdString(part.designator());
data.tacticalIdentifier = QString::fromStdString(part.tacticalIdentifier());
return data;
}
void ParticipantInfoTableModel::onPositionChanged(const ParticipantManagement::ParticipantPosition &position,
DataModification modType)
{
if ((modType != DataModification::eAdded) &&
(modType != DataModification::eModified))
return;
const int row = findRowForId(position.participantId());
if (row < 0)
return;
ParticipantInfo &info = m_vecData[row];
updatePosition(info, position);
emit dataChanged(index(row, Position), index(row, Position), {Qt::BackgroundRole, Qt::DisplayRole});
}
void ParticipantInfoTableModel::onBiteChanged(const ParticipantManagement::Bite &bite,
DataModification modType)
{
if ((modType != DataModification::eAdded) &&
(modType != DataModification::eModified))
return;
const int row = findRowForId(bite.participantId());
if (row < 0)
return;
ParticipantInfo &info = m_vecData[row];
info.biteSampleTime = bite.sampleTime();
info.biteResult = biteStringFromBite(bite);
emit dataChanged(index(row, BiteResult), index(row, BiteResult), {Qt::BackgroundRole, Qt::DisplayRole});
}
void ParticipantInfoTableModel::onPartitionTimeChanged(qint64 newPartitionTime)
{
m_partitionTime = newPartitionTime;
}
void ParticipantInfoTableModel::onPartyChanged(const ParticipantManagement::Party &party,
DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
if (m_ppmGuiCbxIfc)
m_partyIcons[party.id()] = m_ppmGuiCbxIfc->pixmapFromParty(QColor::fromRgb(QRgb(party.color())));
}
void ParticipantInfoTableModel::onTimeoutCheck()
{
for (int i = 0; i < m_vecData.size(); ++i)
{
auto &data = m_vecData[i];
if (data.isOutdated)
continue;
if (isOutdated(data.positionSampleTime))
{
data.isOutdated = true;
const QModelIndex idx(index(i, Position));
emit dataChanged(idx, idx, {Qt::BackgroundRole});
}
}
}
void ParticipantInfoTableModel::fillParticipantContent(ParticipantInfo &info,
const ParticipantManagement::DynamicParticipantData &data) const
{
fillParticipantContent(info, m_ppmDtc->participant(data.participantId()));
}
void ParticipantInfoTableModel::fillParticipantContent(ParticipantInfo &info,
const ParticipantManagement::Participant &part) const
{
info.participantId = part.id();
info.equipmentNumber = part.equipmentNumber().is_set() ? part.equipmentNumber().get() : 0;
info.partyId = part.partyId();
info.typeClass = m_ppmDtc->type(part.typeId()).typeClass();
}
void ParticipantInfoTableModel::updatePosition(ParticipantInfo &info,
const ParticipantManagement::ParticipantPosition &position) const
{
if (!std::isnan(position.pos().lon()) && !std::isnan(position.pos().lat()))
{
info.positionString = LtsCoordTrans::mgrsFromGeographic(position.pos().lon(),
position.pos().lat());
if (!qFuzzyIsNull(position.pos().z()))
info.positionString.append(QStringLiteral(" ") + QString::number(position.pos().z(), 'f', 2));
}
else
info.positionString = tr("Invalid position");
info.positionSampleTime = position.sampleTime();
info.isOutdated = isOutdated(info.positionSampleTime);
}
bool ParticipantInfoTableModel::isOutdated(qint64 sampleTime) const
{
return LtsUtils::isPositionTimeOutdated(m_partitionTime, sampleTime);
}
QString ParticipantInfoTableModel::biteStringFromBite(const ParticipantManagement::Bite &bite) const
{
if (!(bite.sampleTime() > 0))
return tr("--");
bool bitStat = bite.biteState() != bite.configState();
bool batW = bite.batteryState() > 0;
bool confError = bite.configError();
if (!bitStat && !batW && !confError )
return tr("Ok");
QStringList ret;
if (confError)
ret.append(tr("Config Error"));
if (batW)
ret.append(tr("Battery Warning"));
if (bitStat)
ret.append(tr("Bite Error"));
return ret.join(QStringLiteral(", "));
}
QString ParticipantInfoTableModel::vulnerabilityStateString(const ParticipantInfo &data)
{
const ProjectDataManagement::VulnerabilityClass vc = data.typeClass == ParticipantManagement::TypeClass::Human ?
ProjectDataManagement::VulnerabilityClass_def::SoldierInjuries :
ProjectDataManagement::VulnerabilityClass_def::VehicleDamages;
return ProjectDataManagement::toString(data.vulnerabilityState, vc);
}
} // namespace ParticipantManagement