/** * @RDE_HEADER@ * HMI Plugin for Observer Controller - OcParticipantInfo Dialog * $Id$ * $HeadURL$ * $Author$ * Created by Patric Karwat, SLSE, 2018-03-17 */ #include "osc_participant_info.h" #include "ui_osc_participant_info.h" #include #include #include #include #include #include namespace OcSituationClient { OcParticipantInfo::OcParticipantInfo(int participantId, LtsSession::LtsSession *session, QWidget *parent) : LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly) , m_roomId(0) , m_session(session) , m_ui(new Ui::OcParticipantInfo) , m_logger(getLogger("OcOcParticipantInfo")) { m_ui->setupUi(this); m_participant.id(participantId); m_participant.partyId(-1); m_symbolWatcher = new AdvancedSymbology::SymbolResultWatcherLabel(m_ui->lblIcon); //hide first setBuildingPosVisible(false); setSpecialSensorVisible(false); setChargeVisible(false); setKindVisible(false); m_ui->pbHistory->setVisible(false); //config movement track button m_ui->pbMovementTrack->setCheckable(true); //connects connect(m_ui->pbFindOnMap, &QToolButton::clicked, this, &OcParticipantInfo::onFindOnMap); connect(m_ui->pbMovementTrack, &QToolButton::clicked, this, &OcParticipantInfo::onMovementTrack); } OcParticipantInfo::~OcParticipantInfo() { delete m_ui; } bool OcParticipantInfo::init() { if (!m_session->getDtc(m_ppmDtc)) return false; if (!m_session->getDtc(m_pdmDtc)) return false; if (!m_session->getForeignDataInterface(m_asbiIfc)) return false; //optional m_session->getForeignGuiInterface(m_sitMapGuiIfc); m_session->getDtc(m_ismDtc); return setUiValues(); } void OcParticipantInfo::retranslate() { translate(m_ui, tr("Participant Info") ); } void OcParticipantInfo::setRed(QWidget *w, bool bRed) { if (!w) return; if (bRed) { QPalette pal = QApplication::palette(w); pal.setColor(QPalette::Window, Qt::red); w->setPalette(pal); w->setAutoFillBackground(true); } else { // reset to default palette w->setPalette(QApplication::palette(w)); w->setAutoFillBackground(false); } } void OcParticipantInfo::updateIcon(const ParticipantManagement::Participant &participant) { m_symbolWatcher->setSymbolResult(m_asbiIfc->findImage(participant)); } void OcParticipantInfo::updateIcon(const QColor &color, const QString &sidc) { AdvancedSymbology::SymbolDataToRender symDataToRender(sidc); symDataToRender.injury = AdvancedSymbology::Injury::NoInjury; symDataToRender.injuryColor = QColor(); symDataToRender.fillColor = color; symDataToRender.fillColorReplace = true; m_symbolWatcher->setSymbolResult(m_asbiIfc->findImage(symDataToRender)); } bool OcParticipantInfo::setUiValues() { if (!m_ppmDtc || m_participant.id() < 0) return false; //hide findOnMap if no interface available m_ui->pbFindOnMap->setVisible(m_sitMapGuiIfc != nullptr); m_ui->tbNbc->setVisible(false); //show only if visible // m_participant.id() was given within ctor const ParticipantManagement::Participant participantData = m_ppmDtc->participant(m_participant.id()); m_typeClass = m_ppmDtc->type(participantData.typeId()).typeClass(); bool bUpdateDynamicParticipantData = true; switch (m_typeClass.underlying()) { case ParticipantManagement::TypeClass::Human: case ParticipantManagement::TypeClass::GroundVehicle: case ParticipantManagement::TypeClass::VehicleWithGear: case ParticipantManagement::TypeClass::WeaponSystem: case ParticipantManagement::TypeClass::Aircraft: case ParticipantManagement::TypeClass::SupportEntity: { onParticipantChanged(participantData); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged, this, &OcParticipantInfo::onParticipantChanged); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged, this, &OcParticipantInfo::onPositionChanged); onPositionChanged(m_ppmDtc->participantPosition(participantData.id())); if (m_sitMapGuiIfc) { const int32_t participantId = participantData.id(); m_ui->pbFindOnMap->setVisible(true); m_ui->pbMovementTrack->setVisible(true); m_ui->pbMovementTrack->setChecked(m_sitMapGuiIfc->isMovementTrackEnabled(participantId)); // set MovementTrackCallback: std::function &activeSimIds)> onMovementTrackChangedFunc = [this, participantId](const QVector &activeSimIds) { bool checkedNew = activeSimIds.contains(participantId); if (m_ui->pbMovementTrack->isChecked() != checkedNew) { m_ui->pbMovementTrack->setChecked(checkedNew); } }; if (!m_sitMapGuiIfc->setMovementTrackCallback(this, onMovementTrackChangedFunc)) ERROR_LOG(m_logger) << LOGFUNCTION << " setMovementTrackCallback failed"; } break; } case ParticipantManagement::TypeClass::Unit: case ParticipantManagement::TypeClass::SupportUnit: case ParticipantManagement::TypeClass::Vessel: case ParticipantManagement::TypeClass::Unknown: return false; } if (bUpdateDynamicParticipantData) { const auto dpd = m_ppmDtc->dynamicParticipantData(participantData.id()); if (dpd.participantId() || participantData.id()) { onDynamicParticipantDataChanged(dpd); } else { const ParticipantManagement::Party party = m_ppmDtc->party(m_participant.partyId()); const QColor color = QColor::fromRgb(QRgb(party.color())); updateIcon(color, QString::fromStdString(m_participant.symbol())); } } //other connects connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::typeChanged, this, &OcParticipantInfo::onTypeChanged); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged, this, &OcParticipantInfo::onDynamicParticipantDataChanged); connect(m_session, &LtsSession::LtsSession::partitionTimeChanged, this, &OcParticipantInfo::onPartitionTimeChanged); return true; } void OcParticipantInfo::setChargeVisible(bool vis) { m_ui->twGear->setVisible(vis); } void OcParticipantInfo::setSpecialSensorVisible(bool vis) { m_ui->lblSpecialSensorState->setVisible(vis); m_ui->lbSpecialSensorState->setVisible(vis); } void OcParticipantInfo::setBuildingPosVisible(bool vis) { m_ui->lblBuilding->setVisible(vis); m_ui->lblRoom->setVisible(vis); } void OcParticipantInfo::setKindVisible(bool vis) { m_ui->lblKind->setVisible(vis); m_ui->lbKind->setVisible(vis); } void OcParticipantInfo::onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data) { if (data.participantId() != m_participant.id() && data.participantId() != 0) return; //Current State if (data.stateHistory().size() > 0) { const ProjectDataManagement::VulnerabilityClass vc = m_typeClass == ParticipantManagement::TypeClass::Human ? ProjectDataManagement::VulnerabilityClass_def::SoldierInjuries : ProjectDataManagement::VulnerabilityClass_def::VehicleDamages; const ParticipantManagement::StateInformation &lastState = m_ppmDtc->youngestStateInformation(data.stateHistory()); m_ui->lblStatus->setText(ProjectDataManagement::toString(lastState.vulnerabilityState(), vc)); // VBA setKindVisible(lastState.vulnerabilityKindId() > 0); m_ui->lblKind->setText(m_pdmDtc->trVulnerabilityKindName(lastState.vulnerabilityKindId(), vc)); } else { m_ui->lblStatus->setText(QStringLiteral("-")); m_ui->lblKind->setText(QStringLiteral("-")); } setRed(m_ui->lblEquipNr, data.manipulation().size()); // Note: This is not transmitted to O/C. If so, we need to add LEDs for Apache. // So long: Hide, if it's an aircraft. // Special Sensor State if (!data.specialSensorDataState() || m_typeClass == ParticipantManagement::TypeClass_def::Aircraft) { m_ui->lblSpecialSensorState->hide(); m_ui->lbSpecialSensorState->hide(); } else { showSpecialSensorData(data.specialSensorDataState()); m_ui->lbSpecialSensorState->show(); m_ui->lblSpecialSensorState->show(); } //Gear if (data.gear().size() == 0) { m_gearList.clear(); m_ui->twGear->clearContents(); m_ui->twGear->hide(); m_ui->pbGearRanges->hide(); } else if (data.gear() != m_gearList) { m_gearList = data.gear(); std::vector tmpGearList = m_gearList; m_ui->twGear->show(); m_ui->pbGearRanges->show(); m_ui->twGear->clearContents(); int row = 0; int rowCount = tmpGearList.size(); m_ui->twGear->setRowCount(rowCount); m_ui->twGear->setSortingEnabled(false); constexpr int unknownTasId = 0x80000000; for (const ParticipantManagement::GearInformation &gearInfo : m_gearList) { QTableWidgetItem *item = nullptr; if (gearInfo.chargeTypeId() <= 0) //chargTypeId 0 is near miss code { rowCount--; continue; } const int gearTypeId = gearInfo.gearTypeId(); if ((gearTypeId & unknownTasId) == unknownTasId) item = new QTableWidgetItem(tr("Unknown gear id %1").arg(gearTypeId & ~unknownTasId)); else if (gearTypeId == 0) { // Hack for old systems which neither send gearTasCode/gearType nor gearSerialNumber/gearId // 1 = Wanne, 2 = Turm switch (gearInfo.gearId()) { case 1: item = new QTableWidgetItem(tr("Tank", "Gear Type")); break; case 2: item = new QTableWidgetItem(tr("Turret", "Gear Type")); break; default: item = new QTableWidgetItem(tr("N/A", "Gear Type")); break; } } else item = new QTableWidgetItem(m_pdmDtc->trGearTypeName(gearTypeId)); m_ui->twGear->setItem(row, 0, item); if (gearInfo.chargeTypeId() != 0 && gearInfo.chargeTypeId() != -1) // hat Charge (and ignore only chargecounts) { int chargeTypeId = gearInfo.chargeTypeId(); if ((chargeTypeId & unknownTasId) == unknownTasId) item = new QTableWidgetItem(tr("Unknown charge id %1").arg(chargeTypeId & ~unknownTasId)); else if (chargeTypeId == 0) item = new QTableWidgetItem(tr("N/A", "Charge Type")); else { item = new QTableWidgetItem(m_pdmDtc->trChargeTypeShortName(chargeTypeId, true)); item->setToolTip(m_pdmDtc->trChargeTypeName(chargeTypeId, true)); } m_ui->twGear->setItem(row, 1, item); //ressourceCount //: Blank in this case refers to blank ammunition used for training item = new QTableWidgetItem((gearInfo.remain() < 0) ? tr("N/A", "Blank Ammunition") : QString::number(gearInfo.remain())); m_ui->twGear->setItem(row, 2, item); } row++; } m_ui->twGear->setRowCount(rowCount); m_ui->twGear->setSortingEnabled(true); } //end Gear m_ui->tbNbc->setVisible(data.hasNbcProtectionConfigured() && data.hasNbcProtectionActive()); updateIcon(m_participant); } void OcParticipantInfo::showSpecialSensorData(int specialSensorData) { const int specialSensorDataState = specialSensorData & 0xFF; QString strSpecialSensorData; switch (specialSensorDataState) { case ParticipantManagement::SpecialSensorStatus_def::ChemicalDetectionFunction_NotOperational: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::ChemicalDetectionFunction_NotOperational); break; case ParticipantManagement::SpecialSensorStatus_def::ChemicalDetectionFunction_Detecting: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::ChemicalDetectionFunction_Detecting); break; case ParticipantManagement::SpecialSensorStatus_def::NBCDetectionAccessory_NotOperational: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::NBCDetectionAccessory_NotOperational); break; case ParticipantManagement::SpecialSensorStatus_def::NBCDetectionAccessory_Test: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::NBCDetectionAccessory_Test); break; case ParticipantManagement::SpecialSensorStatus_def::NBCDetectionAccessory_DetectingAir: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::NBCDetectionAccessory_DetectingAir); break; case ParticipantManagement::SpecialSensorStatus_def::AVLB_Mounted: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::AVLB_Mounted); break; case ParticipantManagement::SpecialSensorStatus_def::AVLB_Deployed: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::AVLB_Deployed); break; case ParticipantManagement::SpecialSensorStatus_def::AVLB_Mounting: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::AVLB_Mounting); break; case ParticipantManagement::SpecialSensorStatus_def::AVLB_Deploying: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::AVLB_Deploying); break; case ParticipantManagement::SpecialSensorStatus_def::MineLayer_Operational: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineLayer_Operational); break; case ParticipantManagement::SpecialSensorStatus_def::MineLayer_Laying: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineLayer_Laying); break; case ParticipantManagement::SpecialSensorStatus_def::MineClearingFunction_March: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineClearingFunction_March); break; case ParticipantManagement::SpecialSensorStatus_def::MineClearingFunction_ReadyToClear: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineClearingFunction_ReadyToClear); break; case ParticipantManagement::SpecialSensorStatus_def::MineClearingFunction_Clearing: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineClearingFunction_Clearing); break; case ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_NotOperational: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_NotOperational); break; case ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_ParameterInput: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_ParameterInput); break; case ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_ReadyToDeploy: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_ReadyToDeploy); break; case ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_Deploying: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_Deploying); break; case ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_Resupply: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_Resupply); break; case ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_FinishDeploying: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::MineTrowingFunction_FinishDeploying); break; case ParticipantManagement::SpecialSensorStatus_def::ADLER_Operational: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::ADLER_Operational); break; case ParticipantManagement::SpecialSensorStatus_def::ADLER_ConditionallyOperational: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::ADLER_ConditionallyOperational); break; case ParticipantManagement::SpecialSensorStatus_def::ADLER_NotOperational: strSpecialSensorData = toString(ParticipantManagement::SpecialSensorStatus_def::ADLER_NotOperational); break; default: strSpecialSensorData = tr("Unknown (%1)").arg(specialSensorDataState); break; } if ((specialSensorData & (1 << 8)) != 0) strSpecialSensorData += QStringLiteral(" L"); if ((specialSensorData & (1 << 9)) != 0) strSpecialSensorData += QStringLiteral(" R"); if ((specialSensorData & (1 << 10)) != 0) strSpecialSensorData += QStringLiteral(" P"); if ((specialSensorData & (1 << 11)) != 0) strSpecialSensorData += QStringLiteral(" F"); if (specialSensorDataState == 66) // CSF Spürbetrieb { const int type = (specialSensorData >> 12) & 0x0F; strSpecialSensorData += QLatin1Char(' '); switch (type) { case 1: case 2: strSpecialSensorData += tr("FIR"); break; case 3: strSpecialSensorData += tr("ZABC"); break; case 4: strSpecialSensorData += tr("CSF"); break; case 5: strSpecialSensorData += tr("PzBr"); break; case 6: strSpecialSensorData += tr("MV"); break; case 7: strSpecialSensorData += tr("MRF"); break; case 8: strSpecialSensorData += tr("WMV"); break; case 0: default: break; } if (type == 4) // CSF { const int nbcType = (specialSensorData >> 16) & 0x03; strSpecialSensorData += QLatin1Char(' '); switch (nbcType) { case 0: strSpecialSensorData += tr("No C-Agent"); break; case 1: strSpecialSensorData += tr("Sarin"); break; case 2: strSpecialSensorData += tr("VX"); break; case 3: strSpecialSensorData += tr("S-Lost"); break; } } } m_ui->lblSpecialSensorState->setText(strSpecialSensorData); } void OcParticipantInfo::onPositionChanged(const ParticipantManagement::ParticipantPosition &position) { if (position.participantId() != m_participant.id() && position.participantId() != 0) return; onUpdatePosition(position.participantId(), position.pos(), position.sampleTime()); if (m_ismDtc && m_roomId != position.roomId()) { if (position.roomId() > 0) { const auto mse = m_ismDtc->mainStructureElement(position.buildingId()); QString roomName; for (const auto &room : mse.elements()) { if (room.id() == position.roomId()) { roomName = QString::fromStdString(room.displayName()); break; } } QString buildingName = m_ismDtc->getBuildingDisplayName(mse); m_roomId = position.roomId(); m_ui->lblBuilding->setText(buildingName); m_ui->lblRoom->setText(roomName); } setBuildingPosVisible(m_roomId > 0); } if (position.mountedOn().is_set()) { const auto mountedOnParticipant = m_ppmDtc->participant(position.mountedOn().get()); if (mountedOnParticipant.equipmentNumber().is_set()) m_ui->pbMountedOn->setText(QString::number(mountedOnParticipant.equipmentNumber().get())); else m_ui->pbMountedOn->setText(QString::number(0)); m_ui->pbMountedOn->show(); } else m_ui->pbMountedOn->hide(); if (position.mountedParticipants().is_set()) { m_ui->pbMountedParticipants->setText(QString::number(position.mountedParticipants().get().size())); m_ui->pbMountedParticipants->show(); } else m_ui->pbMountedParticipants->hide(); } void OcParticipantInfo::onUpdatePosition(int participantId, const Core::GeoPosition &position, qint64 sampleTime) { m_lastPositionMSec = sampleTime; if ((position.lat() == 0 && position.lon() == 0) || participantId == 0 || std::isnan(position.lon()) || std::isnan(position.lat())) { m_ui->lblPosition->setText(tr("Invalid Position")); } else { QString mgrs = LtsCoordTrans::mgrsFromGeographic(position.lon(), position.lat()); if (!qFuzzyIsNull(position.z())) mgrs += QStringLiteral(" | ") + QString::number(position.z(), 'f', 2); m_ui->lblPosition->setText(mgrs); } } void OcParticipantInfo::onParticipantChanged(const ParticipantManagement::Participant &participant) { if (participant.id() != m_participant.id()) return; if (m_participant.typeId() != participant.typeId()) m_ui->lblType->setText(ParticipantManagement::trTypeName(m_ppmDtc->type(participant.typeId()).name())); if (m_participant.partyId() != participant.partyId()) m_ui->lblParty->setText(ParticipantManagement::trPartyName(m_ppmDtc->party(participant.partyId()).name())); m_ui->lblDesignator->setText(QString::fromStdString(participant.designator())); m_ui->lblTactitcalIdentifier->setText(QString::fromStdString(participant.tacticalIdentifier())); if (participant.equipmentNumber().is_set()) m_ui->lblEquipNr->setText(QString::number(participant.equipmentNumber().get())); else if (participant.isSimulated()) m_ui->lblEquipNr->setText(tr("simulated")); else m_ui->lblEquipNr->setText(QLatin1String("-")); #ifdef LTS_DEBUG m_ui->lblEquipNr->setToolTip(QString::number(participant.id())); #endif m_participant = participant; //hide Info if not exists m_ui->lblInfoText->setText(QString::fromStdString(participant.info())); m_ui->lblInfoText->setVisible(participant.info().size() > 0); m_ui->lblInfo->setVisible(participant.info().size() > 0); m_ui->lblInfo->setText(QString::number(participant.info().size())); onDynamicParticipantDataChanged(m_ppmDtc->dynamicParticipantData(participant.id())); } void OcParticipantInfo::onTypeChanged(const ParticipantManagement::Type &type, DataModification modType) { Q_UNUSED(modType) if (m_participant.typeId() != type.id()) return; m_ui->lblType->setText(ParticipantManagement::trTypeName(type.name())); } void OcParticipantInfo::onPartitionTimeChanged(qint64 newPartitionTime) { setRed(m_ui->lblPosition, LtsUtils::isPositionTimeOutdated(newPartitionTime, m_lastPositionMSec)); //10minuten } void OcParticipantInfo::onFindOnMap() { if (m_sitMapGuiIfc) m_sitMapGuiIfc->centerMapToParticipant(m_participant.id()); } void OcParticipantInfo::onMovementTrack() { if (m_sitMapGuiIfc) m_sitMapGuiIfc->enableMovementTrack(m_participant.id(), m_ui->pbMovementTrack->isChecked()); } } // namespace OcSituationClient