/** * @RDE_HEADER@ * Model for TlnInfo Table * $Id$ * $HeadURL$ * $Author$ * Created by Astrid Mueller, SGAS, 2017-07-10 */ #include "participant_info_table_model.h" #include #include #include #include #include #include #include namespace ParticipantManagement { ParticipantInfoTableModel::ParticipantInfoTableModel(QObject *parent) : LtsWidgets::LtsAbstractTableModel({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 &ppmDtc, bool bWithPositionandBiteResult) { DEBUG_LOG(m_logger) << "**** initModel"; m_session = session; m_ppmDtc = ppmDtc; session->getForeignGuiInterface(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 &selectedIds) { if (selectedIds.isEmpty()) return true; for (int id : selectedIds) removeRow(findRowForId(id)); return true; } void ParticipantInfoTableModel::updateDataView(const QSet &participantIds) { if (participantIds.isEmpty()) return; QSet 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 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 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 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