417 lines
13 KiB
C++
Executable File
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
|