/** * @RDE_HEADER@ * HMI Plugin for EffectShowManagement - Effect Show Overview Dialog Model * $Id$ * $HeadURL$ * $Author$ * Created by Patric Karwat, SLSE, 2016-10.05 */ #include "esm_effect_show_model.h" #include "esm_effect_show_actor_model.h" #include "esm_effect_show_actor_relocation_model.h" #include #include #include #include #include #include #include #include namespace EffectShowManagement { //--------------------------------- // Effect Show Model //--------------------------------- EffectShowModel::EffectShowModel(QObject *parent) : LtsWidgets::LtsAbstractTableModel( { tr("Name AES"), tr("Id"), tr("Fire"), tr("Duration"), tr("Position"), tr("AesStatus"), tr("Actor"), tr("Volley"), tr("Shots in total"), tr("Dimension [m]"), tr("Rotation"), tr("Status"), tr("Answer") }, parent) , m_dtc(nullptr) , m_effectShowActorProxyModel(nullptr) , m_effectShowActorModel(nullptr) , m_effectShowActorRelocationModel(nullptr) , m_logger(getLogger("EffectShowModel")) {} EffectShowModel::~EffectShowModel() { delete m_effectShowActorProxyModel; delete m_effectShowActorModel; delete m_effectShowActorRelocationModel; } bool EffectShowModel::initModel(LtsSession::LtsSession *session) { if (!session->getDtc(m_dtc)) return false; if (!session->getDtc(m_dtcPpm)) return false; if (!session->getDtc(m_dtcAes)) return false; connect(m_dtc.get(), &EffectShowManagement::Interface::Dtc::effectShowChanged, this, &EffectShowModel::onDataChanged); connect(m_dtc.get(), &EffectShowManagement::Interface::Dtc::effectShowVehicleStatusChanged, this, &EffectShowModel::onEffectShowVehicleStatusChanged); // insert Model m_dtc->effectShows([this](const EffectShowManagement::EffectShow & effectShow) { insertRow(effectShow); }); m_effectShowVehicleStatus = m_dtc->effectShowVehicleStatuss(); m_effectShowActorModel = new EffectShowActorModel(this, this); m_effectShowActorProxyModel = new EffectShowActorProxyModel(m_effectShowActorModel, this, this); m_effectShowActorModel->initModel(session); m_effectShowActorRelocationModel = new EffectShowActorRelocationModel(this, this); m_effectShowActorRelocationModel->initModel(m_dtc); m_dtc->effectShowVehicleStatuss([this](const EffectShowManagement::EffectShowVehicleStatus &item) { onEffectShowVehicleStatusChanged(item, DataModification::eAdded);}); return true; } QVariant EffectShowModel::data(const QModelIndex &index, int role) const { if (role != Qt::DisplayRole || !indexIsValid(index)) return QVariant(); const EffectShowManagement::EffectShow &effectShow = m_vecData.at(index.row()); AreaEffectSimulation::AesItem aesItem; m_dtcAes->aesItemFromId(effectShow.idAreaEffectSimulation(), aesItem); switch (index.column()) { case Id: return effectShow.id(); case Name_AES: return QString::fromStdString(effectShow.nameAreaEffectSimulation()); case Duration: return LtsWidgets::readableDuration(effectShow.duration());; case StartTime: return effectShow.startTime().is_set() ? DTG::toStringMSec(effectShow.startTime().get()) : QStringLiteral("---"); case Position: { const Core::GeoPosition2D &pos = effectShow.position(); return LtsCoordTrans::mgrsFromGeographic(pos.lon(), pos.lat()); } case EffectShowActor: { int id = effectShow.esmParticipantId(); return (id > 0) ? getParticipantName(id) : QStringLiteral("---"); } case Volley: { if (effectShow.typeOfAmmunition() == 5) //smoke return QStringLiteral("--"); else return effectShow.numOfVolley(); } case ShotsInTotal: { if (effectShow.typeOfAmmunition() == 5) //smoke return QStringLiteral("--"); else return effectShow.shotsPerVolley(); } case Size: return tr("%1 x %2").arg(QString::number(effectShow.areaWidth()), QString::number(effectShow.areaDepth())); case Rotation: return effectShow.areaRotation(); case Status: return EffectShowManagement::toString(effectShow.orderConfirmation()); case Answer: return QString::fromStdString(effectShow.answerText()); case AesStatus: return AreaEffectSimulation::toString(aesItem.effectStateId()); } return QVariant(); } QMap EffectShowModel::getParticipantList() const { QMap ret; int numRow = m_effectShowActorProxyModel->rowCount(QModelIndex()); for (int i = 0; i < numRow; ++i) { const ParticipantManagement::Participant part = m_effectShowActorModel->getRowData(m_effectShowActorProxyModel->mapToSource(m_effectShowActorProxyModel->index(i, 0)).row()); if (hasVehicleReportedOperationalToday(part.equipmentNumber().get())) ret.insert(part.id(), getParticipantName(part.id())); } return ret; } QMap EffectShowModel::getParticipantList(bool is_smoke) const { QMap ret; int numRow = m_effectShowActorProxyModel->rowCount(QModelIndex()); for (int i = 0; i < numRow; ++i) { int realRow = m_effectShowActorProxyModel->mapToSource(m_effectShowActorProxyModel->index(i, 0)).row(); const ParticipantManagement::Participant part = m_effectShowActorModel->getRowData(realRow); if (!hasVehicleReportedOperationalToday(part.equipmentNumber().get())) continue; EffectShowManagement::Feature_e feature = m_effectShowActorModel->getFeature(realRow); if (feature != EffectShowManagement::Feature_e_def::NotSpecified && ( (feature == EffectShowManagement::Feature_e_def::Smoke && is_smoke) || (feature != EffectShowManagement::Feature_e_def::Smoke && !is_smoke) ) ) ret.insert(part.id(), getParticipantName(part.id())); } return ret; } QVector> EffectShowModel::getParticipantListByDistance(bool is_smoke, const Core::GeoPosition2D &origin) { QVector> ret; //first get the list of all pariticpants and it's name QMap participantList = getParticipantList(is_smoke); //reserve ret.reserve(participantList.size()); QMap distanceMapSorted; for (const auto partId : participantList.keys()) { int eqNr = m_dtcPpm->equipmentNumberByParticipantId(partId); if (eqNr <= 0) continue; EffectShowManagement::EffectShowVehicleStatus vehicleStatus; bool isVehicleStatus = getVehicleStatus(eqNr, vehicleStatus); if (!isVehicleStatus) continue; const ParticipantManagement::ParticipantPosition &pos = m_dtcPpm->participantPosition(partId); //Get Distance to Effect Show (center?) LtsCoordTrans::UtmCoordinate pPos = LtsCoordTrans::UtmCoordinate::fromGeographic(pos.pos().lon(), pos.pos().lat()); LtsCoordTrans::UtmCoordinate pOrigin = LtsCoordTrans::UtmCoordinate::fromGeographic(origin.lon(), origin.lat()); double distance = LtsCoordTrans::distance(pPos, pOrigin); // Save distance and particiapnt Id into temporar map. // the map is sorted so we can use that later instead of writing a sort method vor a vec // for now distanceMapSorted.insert(distance, partId); //get already assigned effect shows QString currentAssigendEffectShows; for (const auto &dat : m_vecData) { if (dat.esmParticipantId() == partId) { if (!currentAssigendEffectShows.isEmpty()) currentAssigendEffectShows.append(QStringLiteral(", ")); currentAssigendEffectShows.append(QString::fromStdString(dat.nameAreaEffectSimulation())); } } //Now we create the new Info String // they want // the Name // Available pyrotechnics/smoke pots (and current load?) // Distance to Target Area // already QString newString = participantList.value(partId); newString.append(QStringLiteral(" ")); newString.append(tr("Mag: %1 | Avail: %2").arg(vehicleStatus.ammunitionMagazine()).arg(vehicleStatus.ammunitionAvailable())); newString.append(tr(" | Dist: %1[m]").arg(QString::number(int(distance)))); if (!currentAssigendEffectShows.isEmpty()) newString.append(tr(" | Assigned: %1").arg(currentAssigendEffectShows)); // Update the String from the original list participantList.insert(partId, newString); } QMapIterator it(distanceMapSorted); while(it.hasNext()) { it.next(); ret.append(QPair(it.value(), participantList.value(it.value()))); } return ret; } QString EffectShowModel::getParticipantName(int id) const { auto it = m_effectShowActorModel->getIt(id); if (it == m_effectShowActorModel->getItEnd()) return QString(); QString eqNumber = QStringLiteral("-"); if (it->equipmentNumber().is_set()) eqNumber = QString::number(it->equipmentNumber().get()); return QStringLiteral("%1 - %2 (%3)").arg(QString::fromStdString(it->designator())).arg(QString::fromStdString(it->tacticalIdentifier())).arg(eqNumber); } QString EffectShowModel::getParticipantAmmunition(int id) const { auto it = m_effectShowActorModel->getIt(id); if (it == m_effectShowActorModel->getItEnd()) return QString(); EffectShowManagement::EffectShowVehicleStatus vehicleStatus; if (!getVehicleStatus(it->equipmentNumber().get(), vehicleStatus)) return QString(); QString res = QString::number(vehicleStatus.ammunitionMagazine()); res.append(QLatin1String(" / ")); res.append(QString::number(vehicleStatus.ammunitionAvailable())); return res; } bool EffectShowModel::isVehicleStatus(int equipmentNum) const { auto it = m_effectShowVehicleStatus.find(equipmentNum); return (it != m_effectShowVehicleStatus.end()); } bool EffectShowModel::getVehicleStatus(int equipmentNum, EffectShowManagement::EffectShowVehicleStatus &vehicleStatus) const { auto it = m_effectShowVehicleStatus.find(equipmentNum); if (it == m_effectShowVehicleStatus.end()) return false; vehicleStatus = it.value(); return true; } bool EffectShowModel::hasVehicleReportedOperationalToday(int equipmentNum) const { EffectShowVehicleStatus vehicleStatus; getVehicleStatus(equipmentNum, vehicleStatus); // vehicle is operational ................................................................................................... if (vehicleStatus.operational()) { DEBUG_LOG(m_logger) << "Effect show actor " << vehicleStatus.nameEffectShowActor() << " with participantId " << vehicleStatus.esmParticipantId() << " is operational."; return true; } // if // vehicle has never reported as operational ................................................................................ if (!m_effectShowVehicleLastTimeOperational.contains(equipmentNum)) { DEBUG_LOG(m_logger) << "Effect show actor " << vehicleStatus.nameEffectShowActor() << " with participantId " << vehicleStatus.esmParticipantId() << " has not reported operational at any time."; return false; } // if // check last time vehicle reported as operational .......................................................................... bool bRet = QDateTime().addMSecs(m_effectShowVehicleLastTimeOperational[equipmentNum]).date() == QDate::currentDate(); if (bRet) DEBUG_LOG(m_logger) << "Effect show actor " << vehicleStatus.nameEffectShowActor() << " with participantId " << vehicleStatus.esmParticipantId() << " is not operational, but has reported operational today."; else DEBUG_LOG(m_logger) << "Effect show actor " << vehicleStatus.nameEffectShowActor() << " with participantId " << vehicleStatus.esmParticipantId() << " is not operational and has not reported operational today."; return bRet; } QString EffectShowModel::getTimeAndPositionOfNextEffectShow(int idParticipant) const { auto it = m_vecData.begin(); if (it == m_vecData.end()) return QString(); qint64 effectStart = 0; int numOrder = 0; Core::GeoPosition2D position; while (it != m_vecData.end()) { if (it->orderConfirmation() != OrderConfirmation_e::OrderCarriedOut && it->esmParticipantId() == idParticipant) { ++numOrder; qint64 start = it->startTime().is_set() ? it->startTime().get() : 0; if (effectStart == 0) { effectStart = start; position = it->position(); } else if (start < effectStart) { effectStart = start; position = it->position(); } } ++it; } if ((numOrder == 0) || ((numOrder > 1) && (effectStart == 0))) return QString(); // no order, or all order without start time QString res; res = (effectStart != 0) ? DTG::toStringMSec(effectStart) : tr("---"); res += QLatin1String(", "); res += LtsCoordTrans::mgrsFromGeographic(position.lon(), position.lat()); return res; } QString EffectShowModel::getTimeAndPositionOfNextActorRelocation(int idParticipant) const { auto it = m_effectShowActorRelocationModel->getItBegin(); if (it == m_effectShowActorRelocationModel->getItEnd()) return QString(); qint64 arrivalTime = std::numeric_limits::max(); Core::GeoPosition2D position; bool found = false; while (it != m_effectShowActorRelocationModel->getItEnd()) { if (it->orderConfirmation() != OrderConfirmation_e::OrderCarriedOut && it->orderConfirmation() != OrderConfirmation_e::PositionReached && it->orderConfirmation() != OrderConfirmation_e::Abort && it->orderConfirmation() != OrderConfirmation_e::Nonacceptance && it->esmParticipantId() == idParticipant) { if (it->arrival_time().get() < arrivalTime) { arrivalTime = it->arrival_time().get(); position = it->position(); found = true; } } ++it; } if (!found) return QString(); QString res; res = (arrivalTime != 0) ? DTG::toStringMSec(arrivalTime) : tr("---"); res += QLatin1String(", "); res += LtsCoordTrans::mgrsFromGeographic(position.lon(), position.lat()); return res; } void EffectShowModel::updateAll() { this->update(); m_effectShowActorModel->update(); m_effectShowActorRelocationModel->update(); } bool EffectShowModel::onEffectShowVehicleStatusChanged(const EffectShowManagement::EffectShowVehicleStatus &data, DataModification modType) { int equipmentNumber = m_dtcPpm->equipmentNumberByParticipantId(data.esmParticipantId()); switch (modType) { case DataModification::eAdded: case DataModification::eModified: if (m_effectShowVehicleStatus.contains(equipmentNumber)) { if (m_effectShowVehicleStatus[equipmentNumber].operational() && !data.operational()) m_effectShowVehicleLastTimeOperational[equipmentNumber] = data.sampleTime(); } // if else m_effectShowVehicleLastTimeOperational[equipmentNumber] = 0; m_effectShowVehicleStatus[equipmentNumber] = data; break; case DataModification::eRemoved: m_effectShowVehicleStatus.remove(equipmentNumber); m_effectShowVehicleLastTimeOperational.remove(equipmentNumber); break; } updateAll(); emit effectShowVehicleStatusChanged(data, modType); return true; } void EffectShowProxyModel::setSourceModel(QAbstractItemModel *sourceModel) { m_sourceModel = qobject_cast(sourceModel); LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel); } void EffectShowProxyModel::setShowExpired(bool show) { if (m_showExpired == show) return; m_showExpired = show; invalidateFilter(); } bool EffectShowProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { if (!m_sourceModel || source_left.column() != source_right.column() || !source_left.isValid() || !source_right.isValid()) return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right); const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row()); const auto dataRight = m_sourceModel->m_vecData.at(source_right.row()); switch (source_left.column()) { case EffectShowModel::StartTime: if (dataLeft.startTime().is_set() && dataRight.startTime().is_set()) return dataLeft.startTime().get() < dataRight.startTime().get(); if (dataRight.startTime().is_set()) return true; if (dataLeft.startTime().is_set()) return false; return dataLeft.id() < dataRight.id(); case EffectShowModel::Name_AES: case EffectShowModel::Id: case EffectShowModel::Duration: case EffectShowModel::Position: case EffectShowModel::EffectShowActor: case EffectShowModel::Volley: case EffectShowModel::ShotsInTotal: case EffectShowModel::Status: case EffectShowModel::Answer: break; } return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right); } bool EffectShowProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { bool bRet = checkFilter(source_row); if (!bRet) return false; return LtsWidgets::LtsColumnFilterProxyModel::filterAcceptsRow(source_row, source_parent); } bool EffectShowProxyModel::checkFilter(int source_row) const { // If showExpired: Show all if (showExpired()) return true; const auto effectShowSource = m_sourceModel->getRowData(source_row); // show when not expired if (effectShowSource.orderConfirmation() != EffectShowManagement::OrderConfirmation_e_def::OrderCarriedOut) return true; return false; } } // namespace EffectShowModel