Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,64 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Hannes Baeuerle, SLSE, 2017-02-24
#
SET(ces_participant_uas_gui_SRCS
logger.cpp
logger.h
ces_participant_uas_gui_plugin.h
ces_participant_uas_gui_plugin.cpp
mission_plan_model.h
mission_plan_model.cpp
mission_plan_edit.h
mission_plan_edit.cpp
mission_plan_dialog.h
mission_plan_dialog.cpp
)
NGL_WRAP_UI (ces_participant_uas_gui_SRCS
mission_plan_dialog.ui
mission_plan_edit.ui
)
NGL_ADD_LIBRARY(WS CesParticipantUASGuiPlugin MODULE ${ces_participant_uas_gui_SRCS})
if (TARGET CesParticipantUASGuiPlugin)
target_sources(CesParticipantUASGuiPlugin
PRIVATE
mission_plan_model.h mission_plan_model.cpp
)
target_sources(CesParticipantUASGuiPlugin
PRIVATE
mission_plan_edit.h mission_plan_edit.cpp mission_plan_edit.ui
)
endif()
NGL_TARGET_LINK_LIBRARIES(CesParticipantUASGuiPlugin
AsbDataPluginInterface
LtsCoordTrans
LtsPrinting
PpmDataPluginInterface
PpmGuiParticipantsPluginInterface
CesDataPluginInterface
GuiPluginInterface
AsmGuiPluginInterface
ExmDataPluginInterface
Qt5::Widgets
PpmCommon
FlmDataPluginInterface
LtsUtilsGuiDds
LtsWidgets
LtsSession
LtsSessionData
LtsDataView
SituationMapGuiPluginInterface
LtsUtilsGuiDds
ltsCoordWidget
LtsUtilsSettings
)
NGL_INSTALL_TARGET(CesParticipantUASGuiPlugin)
@@ -0,0 +1,107 @@
/**
* @RDE_HEADER@
* Gui Plugin for UAS Participant
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#include "ces_participant_uas_gui_plugin.h"
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
#include <Plugins/ParticipantManagement/ppm_gui_combobox_plugin_interface.h>
#include <Services/CentralEffectSimulation/ces_operator_rights.h>
#include <Plugins/CentralEffectSimulation/ces_data_plugin_interface.h>
#include <Plugins/CentralEffectSimulation/Gui/ParticipantUAS/mission_plan_edit.h>
#include "mission_plan_dialog.h"
#include <LtsSession/lts_session.h>
namespace CentralEffectSimulation
{
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
{
return std::make_shared<InterfaceImpl>(session);
}
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
: GuiPluginBase::Interface(session, getLogger("Plugins.Gui.Ces.ParticipantUAS"))
{
}
bool InterfaceImpl::fetchInterfaces()
{
if (!m_session->getDtc<CentralEffectSimulation::DataInterface>(m_cesDtc))
return false;
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
return false;
if (!m_session->getFtc<ParticipantManagement::DataInterface>(m_ppmFtc))
return false;
return true;
}
void InterfaceImpl::clearInterfaces()
{
m_ppmDtc = nullptr;
m_ppmFtc = nullptr;
m_cesDtc = nullptr;
}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
LtsWidgets::PluginData plData;
if (!m_session->isReplayPartition())
{
// UAS LIST
{
plData.menuGroup = menuGroup();
plData.menuText = tr("Mission Plans");
plData.menuIcon = QIcon(QLatin1String("icon:define_movement_simulation_drone.svg"));
plData.identifier = QLatin1String("Mission plans");
plData.operatorRight = OperatorRights::UasControl;
plData.exercisePart = LtsWidgets::ExercisePart::eExecution;
plData.createWidgetFunc = [this](QWidget * parent)
{
return LtsWidgets::initDialog(new MissionPlanDialog(menuGroup(), parent),
m_session);
};
ret.append(plData);
}
// Add UAS
{
plData.menuGroup = menuGroup();
plData.menuText = tr("Add Mission plan");
plData.menuIcon = QIcon(QLatin1String("icon:define_movement_simulation_drone.svg"));
plData.identifier = QLatin1String("ParticipantUAS");
plData.operatorRight = OperatorRights::UasControl;
plData.exercisePart = LtsWidgets::ExercisePart::eExecution;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowAddUASDialog(parent);
};
ret.append(plData);
}
}
return ret;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowAddUASDialog(QWidget *parent)
{
MissionPlanEdit *editWidget = new MissionPlanEdit(parent);
if (editWidget->init(0, m_session))
return editWidget;
delete editWidget;
return nullptr;
}
} // namespace CentralEffectSimulation
@@ -0,0 +1,64 @@
/**
* @RDE_HEADER@
* Gui Plugin for Simulated Movement
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLSE, 2017-12-19
*/
#pragma once
#include <PluginInterfaces/gui_plugin_interface.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <CentralEffectSimulation/ces_dtc.h>
namespace ParticipantManagement
{
class ForeignGuiComboboxInterface;
}
namespace CentralEffectSimulation
{
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:ces.participant_uas")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
explicit GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("CesParticipantUASGui"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override
{
return (partition == IDL::PartitionType::Exercise);
}
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
bool fetchInterfaces() override;
void clearInterfaces() override;
QString menuGroup() const override { return tr("Air Support"); }
QVector<LtsWidgets::PluginData> getActions() override;
LtsWidgets::LtsDialog *onShowAddUASDialog(QWidget *parent);
private: // attributes
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
std::shared_ptr<CentralEffectSimulation::Interface::Dtc> m_cesDtc;
};
} // namespace CentralEffectSimulation
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace CentralEffectSimulation
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "CES.";
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(sizeof(ns) + len);
loggerName.append(ns, sizeof(ns) - 1);
loggerName.append(name, len);
try
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger(loggerName);
}
catch (const std::exception &ex)
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
} // namespace CentralEffectSimulation
@@ -0,0 +1,22 @@
/**
* @RDE_HEADER@
* Logging for CentralEffectSimulation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace CentralEffectSimulation
{
typedef NGL::INGLLogger &LOGGER_T;
LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace CentralEffectSimulation
@@ -0,0 +1,341 @@
/**
* @RDE_HEADER@
* Dialog to Simulate drone
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#include <CentralEffectSimulation/ces_data_plugin_interface.h>
#include <AreaEffectSimulation/aes_data_plugin_interface.h>
#include <ParticipantManagement/ppm_data_plugin_interface.h>
#include <LtsUtilsSettings/settings.h>
#include <CentralEffectSimulation/ces_operator_rights.h>
#include "mission_plan_dialog.h"
#include "ui_mission_plan_dialog.h"
namespace CentralEffectSimulation
{
MissionPlanDialog::MissionPlanDialog(const QString &menuGroup, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_ppmDtc(nullptr)
, m_ppmFtc(nullptr)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_session(nullptr)
, m_menuGroup(menuGroup)
, m_logger(getLogger("MissionPlanDialog"))
, m_ui(new Ui::MissionPlanDialog)
{
m_ui->setupUi(this);
m_updateMissionTimer.setSingleShot(true);
m_updateMissionTimer.setInterval(500);
connect(&m_updateMissionTimer, &QTimer::timeout, this, &MissionPlanDialog::enaDisa);
setOnlineHelpFilename(QStringLiteral("Situation-UAS-Mission"));
}
MissionPlanDialog::~MissionPlanDialog()
{
delete m_ui;
}
bool MissionPlanDialog::init(LtsSession::LtsSession *session)
{
m_session = session;
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
{
ERROR_LOG(m_logger) << " failed to get ppmDtc";
return false;
}
if (!m_session->getFtc<ParticipantManagement::DataInterface>(m_ppmFtc))
{
ERROR_LOG(m_logger) << " failed to get ppmFtc";
return false;
}
m_ppmDtc->enableUnitPosition();
m_ppmDtc->waitForHistoricalDataUnitPosition(1000);
delete m_missionPlanModel;
m_missionPlanModel = new MissionPlanModel(this);
m_missionPlanModel->initModel(m_ppmDtc, m_session->exerciseId());
m_missionPlanProxyModel = new MissionPlanProxyModel(this);
m_missionPlanProxyModel->setSourceModel(m_missionPlanModel);
m_ui->participantsDataView->setModel(m_missionPlanProxyModel);
//Lock Button
m_buttonBox->connectButton(LtsWidgets::DialogButton::Load, this, &MissionPlanDialog::onLockModifications);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setText(tr("Lock"));
m_buttonBox->setIcon(LtsWidgets::DialogButton::Load, QIcon(QStringLiteral("icon:drone_remote_connection.svg")));
//hide for now
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->hide();
//unlock Button
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save, this, &MissionPlanDialog::onUnlockModifications);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Save, false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setText(tr("Unlock"));
m_buttonBox->setIcon(LtsWidgets::DialogButton::Save, QIcon(QStringLiteral("icon:drone_remote_connection.svg")));
//hide for now
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->hide();
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &MissionPlanDialog::onAddMissionPlan); // wird für das Verblockung missbraucht
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Add, true);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &MissionPlanDialog::onEditMissionPlan);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, false);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &MissionPlanDialog::onRemoveMissionPlanClicked);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
connect(m_ui->participantsDataView, &LtsDataView::LtsDataView::signalSelectionChanged, this, &MissionPlanDialog::enaDisa);
const bool &hasOperatorRightParticipantUAS = LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::ParticipantMove);
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setVisible(hasOperatorRightParticipantUAS);
m_buttonBox->getButton(LtsWidgets::DialogButton::Add)->setVisible(hasOperatorRightParticipantUAS);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::missionPlanChanged,
this, &MissionPlanDialog::onMissionPlanChanged);
connect(m_ui->participantsDataView, &LtsDataView::LtsDataView::doubleClicked, this, &MissionPlanDialog::onEditMissionPlan);
return true;
}
void MissionPlanDialog::onModifyMissionPlanReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MissionPlanDialog::onRemoveMissionPlanReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MissionPlanDialog::retranslate()
{
translate(m_ui, tr("UAS Mission Plans"));
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setText(tr("Edit"));
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setText(tr("Delete"));
}
bool MissionPlanDialog::removeMissionPlan(int32_t missionplanId)
{
if (missionplanId <= 0)
return false;
ParticipantManagement::RemoveMissionPlanRequest req(missionplanId);
m_requestHelper->startSingleRequest([req, this]()
{
return m_ppmFtc->removeMissionPlanRequest(req, this);
});
return m_requestHelper->startRequests();
}
MissionPlanDialog::WayPointsStatus MissionPlanDialog::wayPointCurrentStatus(const ParticipantManagement::MissionPlan &mp) const
{
WayPointsStatus state = WayPointsStatus::Undefined;
//prepared
if (mp.missionStart() > m_session->partitionTime())
return WayPointsStatus::Prepared;
else if (mp.itemsFulfilled() != static_cast<int32_t>(mp.items().size()))
return WayPointsStatus::Active;
else if (mp.itemsFulfilled() == static_cast<int32_t>(mp.items().size()))
return WayPointsStatus::Expired;
//aborted
WARNING_LOG(m_logger)<<" -- Status Wegpunktkette Abgebrochen muss definiert werden";
return state;
}
bool MissionPlanDialog::canBeLocked() const
{
auto missionPlan = currentMissionPlan();
if (missionPlan.id()<= 0)
return false;
const auto wpStatus = wayPointCurrentStatus(missionPlan);
const auto uas = m_ppmDtc->getUasByParticipantId(missionPlan.participantId(), m_session->exerciseId());
if (uas.uasState() == ProjectDataManagement::UasState_def::Manual
|| wpStatus == WayPointsStatus::Active )
return true;
return false;
}
bool MissionPlanDialog::canBeUnlocked() const
{
auto missionPlan = currentMissionPlan();
if (missionPlan.id()<= 0)
return false;
if (missionPlan.aborted())
return false;
const auto wpStatus = wayPointCurrentStatus(missionPlan);
const auto uas = m_ppmDtc->getUasByParticipantId(missionPlan.participantId(), m_session->exerciseId());
if ( uas.uasState() == ProjectDataManagement::UasState_def::Manual
|| wpStatus != WayPointsStatus::Active)
return true;
return false;
}
MissionPlanEdit *MissionPlanDialog::currentMissionPlanEditor() const
{
QModelIndexList indexList = m_ui->participantsDataView->selectedRows();
if (indexList.isEmpty())
return nullptr;
const ParticipantManagement::MissionPlan mp = m_missionPlanModel->getRowData(indexList.front().row());
if (mp.id() < 0)
return nullptr;
return m_participantUasEditMap.value(mp.participantId(), nullptr);
}
ParticipantManagement::MissionPlan MissionPlanDialog::currentMissionPlan() const
{
QModelIndexList indexList = m_ui->participantsDataView->selectedRows();
if (indexList.isEmpty())
return {};
return m_missionPlanModel->getRowData(indexList.front().row());
}
void MissionPlanDialog::tableColumnOrganiserSetting()
{
//LtsColumnOrganiser
m_ui->participantsDataView->setColumnsOrganiserEnabled(identifier(), true);
}
void MissionPlanDialog::enaDisa()
{
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, false);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Save, false);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, false); // lock button
QModelIndexList indexList = m_ui->participantsDataView->selectedRows();
bool bOk = !indexList.isEmpty();
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, bOk);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, bOk);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Save, bOk && canBeLocked());
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, bOk && canBeUnlocked()); // lock button
}
void MissionPlanDialog::onUnlockModifications()
{
auto dialog = currentMissionPlanEditor();
if (!dialog)
return;
if (canBeUnlocked())
dialog->setAllUiElementsEnabled(true);
else
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Cannot unlock selected Mission Plan:\n mission plan still activ"));
}
void MissionPlanDialog::onLockModifications()
{
auto dialog = currentMissionPlanEditor();
if (!dialog)
return;
if (canBeLocked())
dialog->setAllUiElementsEnabled(false);
else {
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Cannot Lock selected Mission Plan."));
}
}
void MissionPlanDialog::onAddMissionPlan()
{
static QString newMissionPlanIdentifier(QStringLiteral("NewMissionPlan"));
// selecting an existing mission plan and clicking 'add' must create a new mission plan dialog as a copy of the existing mission plan
const QModelIndexList indexList = m_ui->participantsDataView->selectedRows();
if (indexList.isEmpty())
{
addDialog(LtsWidgets::initDialog(new MissionPlanEdit(this), 0, m_session),
m_menuGroup, newMissionPlanIdentifier, LtsWidgets::LayoutArea::eDialogTab);
return;
}
const ParticipantManagement::MissionPlan mp = m_missionPlanModel->getRowData(indexList.front().row());
if (mp.id() <= 0)
{
return;
}
addDialog(LtsWidgets::initDialog(new MissionPlanEdit(this), mp.id(), m_session, true /*as copy*/),
m_menuGroup, newMissionPlanIdentifier, LtsWidgets::LayoutArea::eDialogTab);
}
void MissionPlanDialog::onRemoveMissionPlanClicked()
{
const QModelIndexList indexList = m_ui->participantsDataView->selectedRows();
const bool isOne = (indexList.size() == 1);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, isOne);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
if (!isOne)
return;
const ParticipantManagement::MissionPlan mp = m_missionPlanModel->getRowData(indexList.front().row());
delete m_participantUasEditMap.value(mp.id());
m_participantUasEditMap.remove(mp.id());
removeMissionPlan(mp.id());
}
void MissionPlanDialog::onEditMissionPlan()
{
QModelIndexList indexList = m_ui->participantsDataView->selectedRows();
if (indexList.isEmpty())
return;
const ParticipantManagement::MissionPlan mp = m_missionPlanModel->getRowData(indexList.front().row());
if (mp.id() <= 0)
return;
const QString dialogIdentifier = QStringLiteral("Edit-MissionPlan_%1").arg(mp.id());
if (!m_participantUasEditMap.contains(mp.participantId()))
{
MissionPlanEdit *dialog = new MissionPlanEdit(this);
m_participantUasEditMap.insert(mp.participantId(), dialog);
connect(dialog, &MissionPlanEdit::editUasDialogClosed, this, &MissionPlanDialog::onEditDialogClosed);
addDialog(LtsWidgets::initDialog(dialog, mp.id(), m_session)
, m_menuGroup
, dialogIdentifier
, LtsWidgets::LayoutArea::eDialogTab);
}
else
focusDialog(m_menuGroup, dialogIdentifier, LtsWidgets::LayoutArea::eDialogTab);
}
void MissionPlanDialog::onEditDialogClosed(const int participantId)
{
m_participantUasEditMap.remove(participantId);
}
void MissionPlanDialog::onMissionPlanChanged(const ParticipantManagement::MissionPlan &data, const DataModification modType)
{
Q_UNUSED(data)
Q_UNUSED(modType)
m_updateMissionTimer.start();
}
} // namespace CentralEffectSimulation
@@ -0,0 +1,105 @@
/**
* @RDE_HEADER@
* Dialog to Simulate drone
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <LtsWidgets/lts_frameless_dialog.h>
#include <LtsSession/lts_session.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <CentralEffectSimulation/ces_dtc.h>
#include <CentralEffectSimulation/ces_ftc.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <Plugins/CentralEffectSimulation/ces_gui_plugin_interface.h>
#include <Plugins/ParticipantManagement/ppm_participants_gui_plugin_interface.h>
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include "mission_plan_model.h"
#include "mission_plan_edit.h"
#include <QTimer>
#include <QWidget>
namespace Ui {
class MissionPlanDialog;
}
namespace CentralEffectSimulation
{
class MissionPlanDialog : public LtsWidgets::LtsDialog
, protected ParticipantManagement::Interface::Ftc::RemoveMissionPlanReplyHandler
, protected ParticipantManagement::Interface::Ftc::ModifyMissionPlanReplyHandler
{
Q_OBJECT
public:
explicit MissionPlanDialog(const QString &menuGroup, QWidget *parent = nullptr);
~MissionPlanDialog();
bool init(LtsSession::LtsSession *session);
void onModifyMissionPlanReply(const Core::FunctionReplyObject &replyObject) override;
void onRemoveMissionPlanReply(const Core::FunctionReplyObject &replyObject) override;
enum class WayPointsStatus
{
Undefined = 0,
Prepared,
Active,
Aborted,
Expired,
};
private:
void retranslate() override;
void changeParty();
bool removeMissionPlan(int32_t missionplanId);
WayPointsStatus wayPointCurrentStatus(const ParticipantManagement::MissionPlan &mp) const;
bool canBeLocked() const;
bool canBeUnlocked() const;
MissionPlanEdit * currentMissionPlanEditor() const;
ParticipantManagement::MissionPlan currentMissionPlan() const ;
void tableColumnOrganiserSetting() override;
protected slots:
void enaDisa();
void onUnlockModifications();
void onLockModifications();
void onAddMissionPlan();
void onRemoveMissionPlanClicked();
void onEditMissionPlan();
void onEditDialogClosed(const int participantId);
void onMissionPlanChanged(const ParticipantManagement::MissionPlan &data, const DataModification modType);
private:
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
LtsUtils::RequestHelper *m_requestHelper;
QTimer m_updateMissionTimer;
LtsSession::LtsSession *m_session;
QString m_menuGroup;
QMap<int, MissionPlanEdit *> m_participantUasEditMap;
LOGGER_T m_logger;
Ui::MissionPlanDialog *m_ui;
MissionPlanModel *m_missionPlanModel{nullptr};
MissionPlanProxyModel *m_missionPlanProxyModel{nullptr};
};
} // namespace CentralEffectSimulation
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MissionPlanDialog</class>
<widget class="QWidget" name="MissionPlanDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1177</width>
<height>290</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="LtsDataView::LtsDataView" name="participantsDataView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,829 @@
/**
* @RDE_HEADER@
* Dialog to edit drone Simulation
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#include <LtsWidgets/lts_message_dialog.h>
#include <Common/LtsCoordTrans/lts_coord_widget.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_data_plugin_interface.h>
#include <Plugins/CentralEffectSimulation/ces_data_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_gui_plugin_interface.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <ParticipantManagement/ppm_translation.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ProjectDataManagement/pdm_translation.h>
#include <ProjectDataManagement/pdm_to_string.h>
#include <FileManagement/flm_translation.h>
#include <LtsSession/lts_session_data.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QTimer>
#include "mission_plan_edit.h"
#include "ui_mission_plan_edit.h"
namespace CentralEffectSimulation
{
//static constexpr double s_msConversion = 3.6; // = 3.6 Convert from [km/h] to [m/s]
//static constexpr int s_timeMilliConversion = 1000; // 1000 Convert from [s] to [ms]
MissionPlanEdit::MissionPlanEdit(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_missionPlanId(0)
, m_uiEnabled(false)
, m_sitMapGuiIfc(nullptr)
, m_ppmCbxGui(nullptr)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_participantTimer(new QTimer(this))
, m_logger(getLogger("MissionPlanEdit"))
, m_ui(new Ui::MissionPlanEdit)
{
m_ui->setupUi(this);
m_ui->nameLineEdit->clear();
m_ui->lePrefix->setText(tr("Waypoint"));
m_uiRequirements->addRequirement({m_ui->lblMoveStart, [this]() { return hasValidEffectStart(m_ui->dteStartTime); }, tr("Begin time is smaller than current time")});
m_uiRequirements->addRequirement({m_ui->lblUASParty, [this]() {return ( !m_ui->cbParty->currentData().isNull());}});
m_uiRequirements->addRequirement({m_ui->lblUasParticipant, [this]() {return ( !m_ui->participantCBox->currentData().isNull());}});
if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
{
m_uiRequirements->addRequirement({m_ui->lblParticipantStateText, [this]() { return m_participantReady; }});
}
connect(this, &MissionPlanEdit::signalFocused, this, &MissionPlanEdit::onFocus);
m_participantTimer->setSingleShot(true);
m_participantTimer->setInterval(500);
connect(m_participantTimer, &QTimer::timeout, this, &MissionPlanEdit::onParticipantTimerTimedout);
setOnlineHelpFilename(QStringLiteral("Situation-UAS-Mission-Edit"));
}
MissionPlanEdit::~MissionPlanEdit()
{
m_sitMapGuiIfc = nullptr;
delete m_ui;
emit editUasDialogClosed(m_participantData.id());
}
bool MissionPlanEdit::init(const int missionPlanId, LtsSession::LtsSessionDataDds *session, bool asCopy)
{
m_session = session;
m_missionPlanId = missionPlanId;
if (!m_session->getDtc<ProjectDataManagement::DataInterface>(m_pdmDtc))
{
ERROR_LOG(m_logger) << " failed to get pdmDtc";
return false;
}
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
{
ERROR_LOG(m_logger) << " failed to get ppmDtc";
return false;
}
if (!m_session->getFtc<ParticipantManagement::DataInterface>(m_ppmFtc))
{
ERROR_LOG(m_logger) << " failed to get ppmFtc";
return false;
}
if (!m_session->getForeignGuiInterface<ParticipantManagement::ForeignGuiComboboxInterface>(m_ppmCbxGui))
{
ERROR_LOG(m_logger) << " failed to get ppmCbxGui";
return false;
}
// Hide Unused Buttons for now
m_ui->cbJumpToStart->hide();
m_ui->cbRepeat->hide();
//m_ui->chargesCBox->hide();
//m_ui->chargesLabel->hide();
m_session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(m_sitMapGuiIfc);
m_buttonBox->setText(LtsWidgets::DialogButton::Edit, tr("Start Now"));
m_buttonBox->setIcon(LtsWidgets::DialogButton::Edit, QIcon(QStringLiteral("icon:media-play.svg")));
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &MissionPlanEdit::onApplyNow);
m_missionPlan = m_ppmDtc->missionPlan(m_missionPlanId);
auto cb = [this, asCopy](const ParticipantManagement::MissionPlan &mission)
{
//allow participant id of current mission to be displayed.
//for other missions if mission start is in future, and not abortet uas participants will not be displayed in the combobox
if ((((mission.participantId() == m_missionPlan.participantId()) && !asCopy ) || mission.participantId() <= 0)
&& mission.missionStart() <= m_missionPlan.missionStart() && !mission.aborted())
return;
// if mission is completed or aborted uas participant should be available.
if (static_cast<int>(mission.items().size()) == mission.itemsFulfilled() || mission.aborted())
return;
m_usedUasSet.insert(mission.participantId());
};
m_ppmDtc->missionPlans(cb);
m_ppmDtc->waitForHistoricalDataParty(1000);
m_ppmDtc->waitForHistoricalDataUAS(1000);
m_ppmDtc->waitForHistoricalDataParticipant(1000);
connect(m_ui->participantCBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int /*index*/)
{
setReady(false);
const auto pId = m_ui->participantCBox->currentData().toInt();
m_participant = m_ppmDtc->participant(pId);
const auto dpd = m_ppmDtc->dynamicParticipantData(m_participant.id());
DEBUG_LOG(m_logger) << "idx changed! pId: " << pId << ", m_participant.id(): " << m_participant.id() << ", dpd.participantId(): " << dpd.participantId();
if (dpd.participantId() < 1) // aka. no data for that uas yet, aka. not ready
{
ERROR_LOG(m_logger) << "no dynamic pdata yet for pId " << pId;
setReady(false);
return;
}
DEBUG_LOG(m_logger) << "calling onDynamicParticipantDataChanged !";
onDynamicParticipantDataChanged(dpd, DataModification::eAdded);
});
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged,
this, &MissionPlanEdit::onDynamicParticipantDataChanged);
connect(m_ui->cbParty, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MissionPlanEdit::onPartyChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::uASChanged,
this, &MissionPlanEdit::onUasChanged);
// set Party & UAS Participants Combobox
fillParties();
setUASCharges();
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::missionPlanChanged,
this, &MissionPlanEdit::onMissionPlanChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
this, &MissionPlanEdit::onParticipantChanged);
if (m_sitMapGuiIfc)
{
connect(m_ui->pbPosition, &QPushButton::clicked, this, &MissionPlanEdit::onGetPolyline);
connect(m_ui->pbClear, &QPushButton::clicked, this, &MissionPlanEdit::clearCoordinates);
}
else
{
m_ui->pbPosition->setEnabled(false);
m_ui->pbClear->setEnabled(false);
}
// use missionplan data as default if exist
setData(missionPlanId);
enaDisa();
if(m_participant.id() <= 0 )
setReady(false);
if (m_missionPlanId > 0 && m_session->partitionTime() > m_missionPlan.missionStart() && !asCopy)
{
enableUiElements(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setEnabled(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setEnabled(false);
}
if (asCopy)
{
m_missionPlanId = 0; // mark the edit dialog as new, which makes it a create dialog
// if the UAS from the copy already is in used we need to reset to default again
if (m_ui->participantCBox->currentIndex()<0)
m_ui->participantCBox->setCurrentIndex(0);
}
return true;
}
void MissionPlanEdit::retranslate()
{
translate(m_ui, ((m_missionPlanId <= 0) ? QString() : QString::fromStdString(m_missionPlan.name())), m_missionPlanId <= 0, tr("UAS-Waypoints"));
}
void MissionPlanEdit::setReady(bool isReady)
{
m_participantReady = isReady;
m_ui->lblParticipantState->setText(QStringLiteral("<span style=\"color:#%1;\">%2</span>")
.arg(isReady ? QStringLiteral("00dd00") : QStringLiteral("dd0000"))
.arg(isReady ? tr("Ready") : tr("Not ready")));
}
// stolen from Lts/Plugins/ParticipantManagement/Gui/ParticipantInfo/participant_info_dialog.cpp
void MissionPlanEdit::onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType)
{
if (data.participantId() != m_participant.id() && data.participantId() != 0)
{
ERROR_LOG(m_logger) << "### " << data.participantId() << " != " << m_participant.id() << " (" << (int)modType << ")";
return;
}
DEBUG_LOG(m_logger) << "### " << data.participantId() << " == " << m_participant.id() << " (" << (int)modType << ") !!!";
// Do not continue on modType == DataModification::eRemove
// otherwise for example the icon would look bad (clover with black on black...)
if (modType == DataModification::eRemoved)
{
return;
}
//Current State
if (data.stateHistory().empty())
{
setReady(false);
return;
}
const auto &history = data.stateHistory();
//LasState is the last "real" state, that has a VBG <=8 (Castastrophic)
//It's not the last state received, as there can be other state changes like Sanitation, or NearMiss
auto lastState = m_ppmDtc->youngestStateInformation(history/*, true*/ /* debug */);
decltype(lastState) youngestState;
youngestState.stateTime(0);
for (const auto &si : history)
{
DEBUG_LOG(m_logger) << "StateInformation "
<< " time=" << si.stateTime()
<< " vulnerabilityBits=" << si.vulnerabilityBits()
<< " vulnerabilityKindId=" << si.vulnerabilityKindId();
if ( youngestState.stateTime() < si.stateTime())
youngestState = si;
}
setReady(youngestState.vulnerabilityState() == ProjectDataManagement::VulnerabilityState::CombatReady);
}
void MissionPlanEdit::onMissionPlanChanged(const ParticipantManagement::MissionPlan &data, const DataModification modType)
{
Q_UNUSED(modType)
if (data.id() != m_missionPlanId)
{
// Check, if mission is completed
if (static_cast<int>(data.items().size()) != data.itemsFulfilled())
return;
if (m_usedUasSet.contains(data.participantId()))
{
m_usedUasSet.remove(data.participantId());
fillParticipants();
}
return;
}
bool enabled;
if (m_session->partitionTime() > m_missionPlan.missionStart() && !m_missionPlan.aborted())
enabled = false;
else
enabled = true;
enableUiElements(enabled);
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setEnabled(enabled);
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled(enabled);
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setEnabled(enabled);
if (static_cast<int>(data.items().size()) == data.itemsFulfilled()
&& m_usedUasSet.remove(data.participantId()))
fillParticipants();
}
void MissionPlanEdit::onModifyMissionPlanReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
if (!replyObject.error())
m_sitMapGuiIfc->removePreviewObject();
}
void MissionPlanEdit::onSetMissionActionFulfilledReply(const Core::FunctionReplyObject &replyObject)
{
DEBUG_LOG(m_logger)<<" -- MissionPlanEdit - onSetMissionActionFulfilledReply: "<<replyObject.error();
}
QStringList MissionPlanEdit::tableColLabelList() const
{
QStringList wayPointsHeaders;
wayPointsHeaders << tr("Identifier")
<< tr("Position")
<< tr("Altitude")
<< tr("Idle Time [s]")
<< tr("Charges");
return wayPointsHeaders;
}
void MissionPlanEdit::setAllUiElementsEnabled(bool enable)
{
enableUiElements(enable);
}
ParticipantManagement::MissionPlan MissionPlanEdit::missionPlan() const
{
return m_missionPlan;
}
bool MissionPlanEdit::isUiEnabled() const
{
return m_uiEnabled;
}
void MissionPlanEdit::enaDisa()
{
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setEnabled((m_ui->tableWidget->rowCount() > 0));
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled((m_ui->tableWidget->rowCount() > 0));
}
bool MissionPlanEdit::apply()
{
// check uas switched to manual mode before proceeding
ParticipantManagement::UAS uasP = m_ppmDtc->getUasByParticipantId(m_ui->participantCBox->currentData().toInt(), m_session->exerciseId());
if (uasP.uasState() == ProjectDataManagement::UasState_def::Manual
&& uasP.participantId() != m_missionPlan.participantId())
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Waypoints can't be saved!: Uas switched to %1-Mode").arg(ProjectDataManagement::toString(uasP.uasState())));
return false;
}
ParticipantManagement::ModifyMissionPlanRequest req;
req.id(m_missionPlanId);
req.missionStart(m_ui->dteStartTime->msecsSinceEpoch());
req.participantId(m_ui->participantCBox->currentData().toInt());
req.jumpToStart(false);
req.repeat(false);
req.name(m_ui->nameLineEdit->text().toStdString());
req.items(itemsRequestContent());
return m_requestHelper->startSingleRequest([this, req]()
{
return m_ppmFtc->modifyMissionPlanRequest(req, this);
});
}
void MissionPlanEdit::clearCoordinates()
{
if (m_ui)
{
m_ui->tableWidget->clearContents();
m_ui->tableWidget->setRowCount(0);
enaDisa();
}
// remove PreviewObject:
if ( m_sitMapGuiIfc && this->amIFocused())
m_sitMapGuiIfc->removePreviewObject();
}
void MissionPlanEdit::clearContent()
{
m_ui->lePrefix->clear();
m_ui->cbRepeat->setChecked(false);
//m_ui->cbJump->setChecked(false);
clearCoordinates();
}
void MissionPlanEdit::enableUiElements(bool enable)
{
m_ui->tableWidget->setEnabled(enable);
m_ui->pbPosition->setEnabled(enable);
m_ui->pbClear->setEnabled(enable);
m_ui->spIdleTime->setEnabled(enable);
m_ui->dteStartTime->setEnabled(enable);
m_ui->cbParty->setEnabled(enable);
m_ui->participantCBox->setEnabled(enable);
m_ui->chargesCBox->setEnabled(enable);
m_ui->nameLineEdit->setEnabled(enable);
m_ui->lePrefix->setEnabled(enable);
m_ui->cbRepeat->setEnabled(enable);
m_ui->cbJumpToStart->setEnabled(enable);
m_ui->spAltitude->setEnabled(false);
m_uiEnabled = enable;
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setEnabled((m_ui->tableWidget->rowCount() > 0 && !enable)); // in case edit is blocked by user
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled((m_ui->tableWidget->rowCount() > 0 && !enable)); // in case edit is blocked by user
}
bool MissionPlanEdit::hasValidEffectStart(LtsWidgets::LtsDateTimeEdit *dte) const
{ // if there is visible a enabled/disable checkbox -> also allow 0 as effect start
const bool bEmptyAllowed = dte->isCheckable();
const auto ms = dte->msecsSinceEpoch();
// no effect start -> ok
if (bEmptyAllowed && ms == 0)
return true;
return ms > m_session->partitionTime();
}
void MissionPlanEdit::initTableHeader()
{
m_ui->tableWidget->setColumnCount(tableColLabelList().count());
m_ui->tableWidget->setHorizontalHeaderLabels(tableColLabelList());
m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}
void MissionPlanEdit::insertPos(const WayPointParam &wpp)
{
const int rowCount = m_ui->tableWidget->rowCount();
// insert waypoint label
m_ui->tableWidget->insertRow(rowCount);
QLineEdit *le = new QLineEdit;
le->setText(wpp.identifier);
m_ui->tableWidget->setCellWidget(rowCount, Name, le);
//insert Coordinates
LtsCoordWidget::LtsCoordWidget *wdg = new LtsCoordWidget::LtsCoordWidget();
wdg->setCoordinate(wpp.lon, wpp.lat);
wdg->setObjectName(QStringLiteral("positionEdit_") + QString::number(rowCount));
wdg->setDisplayType(LtsCoordWidget::LtsCoordWidget::DisplayType::MGRS);
m_ui->tableWidget->setCellWidget(rowCount, Position, wdg);
// insert Altitude [m]
QSpinBox *sbAltitude = new QSpinBox;
sbAltitude->setMinimum(0);
sbAltitude->setMaximum(120);
sbAltitude->setValue(wpp.z);
m_ui->tableWidget->setCellWidget(rowCount, Altitude, sbAltitude);
// insert Idle Time [sec]
QSpinBox *sbDelay = new QSpinBox;
sbDelay->setMinimum(m_ui->spIdleTime->minimum());
sbDelay->setMaximum(m_ui->spIdleTime->maximum());
sbDelay->setValue(wpp.idleTime);
m_ui->tableWidget->setCellWidget(rowCount, IdleTime, sbDelay);
// insert charges
QComboBox *ccbox = new QComboBox;
ccbox->insertItem(m_ui->chargesCBox->count(), tr("Undefined"));
for(const auto &c : m_uasCharges.toStdMap())
{
ccbox->addItem(c.second, c.first);
}
int idx = ccbox->findData(QVariant::fromValue(wpp.chargeTypeId));
ccbox->setCurrentIndex(idx > 0 ? idx : 0 );
m_ui->tableWidget->setCellWidget(rowCount, Charges, ccbox);
}
void MissionPlanEdit::showPreview(bool show)
{
if (m_sitMapGuiIfc)
{
if (show)
m_sitMapGuiIfc->displayPreviewObject(this, m_polyline, false);
else
m_sitMapGuiIfc->removePreviewObject();
}
}
const std::vector<ParticipantManagement::MissionItem> MissionPlanEdit::itemsRequestContent()
{
const int missionItemCount = m_ui->tableWidget->rowCount();
std::vector<ParticipantManagement::MissionItem> missionItems;
using ParticipantManagement::MissionActionType;
const QVector<MissionActionType> actiontypes{
MissionActionType::MoveTo, MissionActionType::Loiter, MissionActionType::Attack}; // first action always MoveTo
int itemNumber = 0;
for (int i = 0; i < missionItemCount; i++)
{
ParticipantManagement::MissionItem msi;
QWidget *nameWidget = m_ui->tableWidget->cellWidget(i, Name);
const QString name = static_cast<QLineEdit *>(nameWidget)->text();
QWidget *altitudeWidget = m_ui->tableWidget->cellWidget(i, Altitude);
const int altitude = static_cast<QSpinBox *>(altitudeWidget)->value();
QWidget *posWidget = m_ui->tableWidget->cellWidget(i, Position);
const QPointF point = static_cast<LtsCoordWidget::LtsCoordWidget *>(posWidget)->coordinate();
Core::GeoPosition geoPos(point.x(), point.y(), altitude);
QWidget *idleTimeWidget = m_ui->tableWidget->cellWidget(i, IdleTime);
const int idleTime = static_cast<QSpinBox *>(idleTimeWidget)->value();
QWidget *chargesWidget = m_ui->tableWidget->cellWidget(i, Charges);
const int chargeTypeId = static_cast<QComboBox *>(chargesWidget)->currentData().toInt();
// phase 1 // moveTo
// phase 2 // idleTime move
// phase 3 // attack
const QString &msiName = name;
int actionNumber = 0;
const QString &msaName = QStringLiteral("%1_%2").arg(m_ui->nameLineEdit->text()).arg(name);
{
ParticipantManagement::MissionAction actionMoveTo;
actionMoveTo.actionType(ParticipantManagement::MissionActionType::MoveTo);
actionMoveTo.duration(0); // not define hier
actionMoveTo.actionNumber(actionNumber++);
actionMoveTo.name(msaName.toStdString());
actionMoveTo.itemNumber(itemNumber);
actionMoveTo.planId(m_missionPlan.id());
QJsonObject jsoMoveTo;
jsoMoveTo.insert(QStringLiteral("location_x"), point.x());
jsoMoveTo.insert(QStringLiteral("location_y"), point.y());
jsoMoveTo.insert(QStringLiteral("location_z"), altitude); //
const QString props = QString::fromUtf8(QJsonDocument(jsoMoveTo).toJson(QJsonDocument::Compact));
actionMoveTo.properties(props.toStdString());
msi.actions().push_back(actionMoveTo);
}
if (idleTime > 0)
{
ParticipantManagement::MissionAction actionLoiter;
actionLoiter.actionType(ParticipantManagement::MissionActionType::Loiter);
actionLoiter.duration(idleTime); // idleTime
actionLoiter.actionNumber(actionNumber++);
actionLoiter.itemNumber(itemNumber);
actionLoiter.name(msaName.toStdString());
actionLoiter.planId(m_missionPlan.id());
msi.actions().push_back(actionLoiter);
}
if (chargeTypeId > 0)
{
ParticipantManagement::MissionAction actionAttack;
actionAttack.actionType(ParticipantManagement::MissionActionType::Attack);
actionAttack.duration(idleTime); // idleTime
actionAttack.actionNumber(actionNumber++);
actionAttack.itemNumber(itemNumber);
actionAttack.name(msaName.toStdString());
actionAttack.planId(m_missionPlan.id());
QJsonObject jsoAttack;
jsoAttack.insert(QStringLiteral("location_x"), point.x());
jsoAttack.insert(QStringLiteral("location_y"), point.y());
jsoAttack.insert(QStringLiteral("location_z"), 0); //
jsoAttack.insert(QStringLiteral("chargeTypeId"), chargeTypeId); // chargeTypeID
const QString props = QString::fromUtf8(QJsonDocument(jsoAttack).toJson(QJsonDocument::Compact));
actionAttack.properties(props.toStdString());
msi.actions().push_back(actionAttack);
}
msi.itemNumber(itemNumber++);
msi.location(geoPos);
msi.name(msiName.toStdString());
missionItems.push_back(msi);
}
return missionItems;
}
void MissionPlanEdit::setUASCharges()
{
const auto chargesForGearClass = m_pdmDtc->chargeTypes();
m_ui->chargesCBox->clear();
m_ui->chargesCBox->insertItem(m_ui->chargesCBox->count(), tr("Undefined"));
for (const ProjectDataManagement::ChargeType &chargeType : qAsConst(chargesForGearClass))
{
// NOTE: igniterType/IgniterFunciton = 0 as long as MissionPlan does not contain values
if (!m_pdmDtc->isUasDroppable(chargeType.id(),0,0))
continue;
DEBUG_LOG(m_logger) << " fillCharges"
<< ", id=" << chargeType.id()
<< ", name= " << chargeType.name();
m_ui->chargesCBox->addItem(QString::fromStdString(chargeType.name()), chargeType.id());
m_uasCharges.insert(chargeType.id(), QString::fromStdString(chargeType.name()));
}
m_ui->chargesCBox->setCurrentIndex(0);
}
void MissionPlanEdit::setData(int mpId)
{
initTableHeader();
if (mpId < 0 )
return;
m_missionPlan = m_ppmDtc->missionPlan(mpId);
m_participantData = m_ppmDtc->participant(m_missionPlan.participantId());
m_ui->dteStartTime->setMinimumTimeMSec(m_session->partitionTime());
m_ui->dteStartTime->setRetrieveCurrentTimeCallback([this](){return m_session->partitionTime();});
m_ui->dteStartTime->setMsecsSinceEpoch(m_missionPlan.missionStart());
m_ui->nameLineEdit->setText(QString::fromStdString(m_missionPlan.name()));
// get/set current party
if (m_participantData.partyId() > 0)
{
const int partyIdx = m_ui->cbParty->findData(QVariant::fromValue(m_participantData.partyId()));
m_ui->cbParty->setCurrentIndex(partyIdx);
const int ppIdx = m_ui->participantCBox->findData(QVariant::fromValue(m_missionPlan.participantId()));
m_ui->participantCBox->setCurrentIndex(ppIdx);
}
for (const auto &item : qAsConst(m_missionPlan.items()) )
{
const std::vector<ParticipantManagement::MissionAction> &msaVec = item.actions();
WayPointParam wpp;
wpp.identifier = QString::fromStdString(item.name());
wpp.lat = item.location().lat();
wpp.lon = item.location().lon();
wpp.z = item.location().z();
for (auto const &msa : msaVec)
{
if (msa.actionType() == ParticipantManagement::MissionActionType::Loiter)
wpp.idleTime = msa.duration();
if (msa.actionType() == ParticipantManagement::MissionActionType::Attack)
{
const QJsonObject &obj = QJsonDocument::fromJson(QByteArray::fromStdString(msa.properties())).object();
wpp.chargeTypeId = obj.value(QLatin1String("chargeTypeId")).toInt();
}
}
insertPos(wpp);
}
//
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setEnabled((m_ui->tableWidget->rowCount() > 0));
}
void MissionPlanEdit::fillParties()
{
m_ppmCbxGui->fillPartyComboBox(m_ui->cbParty);
m_ui->cbParty->setCurrentIndex(0);
fillParticipants();
}
void MissionPlanEdit::fillParticipants()
{
QVector<ParticipantManagement::Participant> participants;
const int partyId = m_ui->cbParty->currentData().toInt();
gatherUASParticipants(partyId, participants);
m_ui->participantCBox->clear();
for (const auto &participant : participants)
{
QString uasString = QStringLiteral("%1 (%2)").arg(m_ppmDtc->hierarchyFromParticipant(participant.id()));
if (uasString.isEmpty() || m_usedUasSet.contains(participant.id()))
continue;
const ParticipantManagement::UAS &uasP = m_ppmDtc->getUasByParticipantId(participant.id(), m_session->exerciseId());
uasString = uasString.arg(ProjectDataManagement::toString(uasP.uasState()));
m_ui->participantCBox->addItem(uasString, participant.id());
}
if (m_missionPlan.id() > 0)
m_ui->participantCBox->setCurrentIndex(m_ui->participantCBox->findData(QVariant::fromValue(m_missionPlan.participantId())));
//Fallback to first entry
if (m_ui->participantCBox->currentIndex()<0)
m_ui->participantCBox->setCurrentIndex(0);
}
void MissionPlanEdit::gatherUASParticipants(int partyId, QVector<ParticipantManagement::Participant> &participant)
{
auto cb = [this, &partyId, &participant](const ParticipantManagement::Participant &p)
{
if (p.isDeleted() || p.partyId() != partyId)
return;
const ParticipantManagement::ParticipantData &ppData = m_ppmDtc->convertParticipantToParticipantData(p);
const ParticipantManagement::Type &&ppType = m_ppmDtc->type(ppData.typeId());
const QJsonObject typeProperties(QJsonDocument::fromJson(QByteArray::fromStdString(ppType.properties())).object());
if (typeProperties.isEmpty()
|| ppType.typeClass() != ParticipantManagement::TypeClass::Aircraft
|| !typeProperties[QStringLiteral("isUAS")].toBool(false))
return;
participant.push_back(p);
};
m_ppmDtc->participants(cb);
}
/*!
* \brief ParticipantUASEdit::onParticipantTimerTimedout
* method is called when an participant is updated. Note that onParticipantTimerTimedout()
* will be called by once even if many onParticipantChanged signal come in.
*/
void MissionPlanEdit::onParticipantTimerTimedout()
{
fillParticipants();
}
void MissionPlanEdit::onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType)
{
Q_UNUSED(data)
Q_UNUSED(modType)
m_participantTimer->start();
}
void MissionPlanEdit::onPartyChanged()
{
m_ui->participantCBox->clear();
fillParticipants();
}
void MissionPlanEdit::onGetPolyline()
{
if (!m_sitMapGuiIfc)
return;
if (m_sitMapGuiIfc->mapMode() != SituationMap::MapMode::MapMode_2D)
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Only available in 2D mode"));
return;
}
QPolygonF coordinates;
int i = 0;
while (true)
{
LtsCoordWidget::LtsCoordWidget *w = findChild<LtsCoordWidget::LtsCoordWidget *>(QStringLiteral("positionEdit_%1").arg(i));
if (!w)
break;
if (!w->coordinate().isNull())
coordinates << w->coordinate();
++i;
}
m_sitMapGuiIfc->getPolyline(this,
SLOT(onPolylineData(QPolygonF)),
nullptr,
2,
11,
-1,
-1,
coordinates);
}
void MissionPlanEdit::onPolylineData(const QPolygonF &polyline)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " numberOfPoints: " << polyline.size();
if (polyline.size() < 2)
return;
m_polyline = polyline;
clearCoordinates();
for (int i = 0; i < polyline.size(); i++)
{
WayPointParam wp;
wp.lon = polyline.at(i).x();
wp.lat = polyline.at(i).y();
wp.identifier = m_ui->lePrefix->text() + QLatin1Char('_') + QString::number(i + 1);
wp.z = m_ui->spAltitude->value();
wp.idleTime = m_ui->spIdleTime->value();
wp.chargeTypeId = m_ui->chargesCBox->currentIndex();
insertPos(wp);
}
// create PreviewObject:
m_sitMapGuiIfc->displayPreviewObject(this, polyline, false);
enaDisa();
}
void MissionPlanEdit::onFocus()
{
showPreview(true);
m_sitMapGuiIfc->displayPreviewObject(this, m_polyline, false);
}
void MissionPlanEdit::onApplyNow()
{
QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(m_session->partitionTime());
m_ui->dteStartTime->setMsecsSinceEpoch(dateTime.addSecs(10).toMSecsSinceEpoch()); //set time first THEN check for requirements
if (m_ui->tableWidget->rowCount() < 2 || !m_uiRequirements->checkRequirements())
return;
if (apply())
{
enableUiElements(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Edit)->setEnabled(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setEnabled(false);
}
}
void MissionPlanEdit::onUasChanged(const ParticipantManagement::UAS &uas)
{
Q_UNUSED(uas)
const int ppId = m_ui->participantCBox->currentData().toInt();
fillParticipants();
const int index = m_ui->participantCBox->findData(ppId);
if( index < 0)
return;
m_ui->participantCBox->setCurrentIndex(index);
}
}// namspace CentralEffectSimulation
@@ -0,0 +1,137 @@
/**
* @RDE_HEADER@
* Dialog to edit drone Simulation
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <LtsWidgets/lts_frameless_dialog.h>
#include <LtsSession/lts_session.h>
#include <CentralEffectSimulation/ces_ftc.h>
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
#include <Plugins/ParticipantManagement/ppm_gui_combobox_plugin_interface.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <CentralEffectSimulation/ces_dtc.h>
#include <LtsWidgets/lts_date_time_edit.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include "logger.h"
#include <QWidget>
namespace Ui {
class MissionPlanEdit;
}
namespace CentralEffectSimulation
{
struct WayPointParam
{
double lon;
double lat;
double z = 0.0;
int idleTime = 0;
double speed = 0.0;
int chargeTypeId = -1;
QString identifier;
};
class MissionPlanEdit : public LtsWidgets::LtsDialog
, protected ParticipantManagement::Interface::Ftc::ModifyMissionPlanReplyHandler
, protected ParticipantManagement::Interface::Ftc::SetMissionActionFulfilledReplyHandler
{
Q_OBJECT
public:
enum SegmentColumns
{
Name,
Position,
Altitude,
IdleTime, // [sec]
Charges
};
explicit MissionPlanEdit(QWidget *parent = nullptr);
~MissionPlanEdit();
bool init(const int missionPlanId, LtsSession::LtsSessionDataDds *session, bool asCopy = false);
void retranslate() override;
void onModifyMissionPlanReply(const Core::FunctionReplyObject &replyObject) override;
void onSetMissionActionFulfilledReply(const Core::FunctionReplyObject &replyObject) override;
QStringList tableColLabelList() const;
void setAllUiElementsEnabled(bool enable);
ParticipantManagement::MissionPlan missionPlan() const;
bool isUiEnabled() const;
signals:
void editUasDialogClosed(const int participantId);
private:
void enaDisa();
bool apply() override;
void clearCoordinates();
void clearContent();
void enableUiElements(bool enable);
bool hasValidEffectStart(LtsWidgets::LtsDateTimeEdit *dte) const;
void initTableHeader();
void insertPos(const WayPointParam &wpp);
void showPreview(bool show);
const std::vector<ParticipantManagement::MissionItem> itemsRequestContent();
void setUASCharges();
void setData(int mpId);
void fillParties();
void fillParticipants();
void gatherUASParticipants(int partyId, QVector<ParticipantManagement::Participant> &participant);
void setReady(bool isReady);
private slots:
void onParticipantTimerTimedout();
void onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType);
void onPartyChanged();
void onGetPolyline();
void onPolylineData(const QPolygonF &polyline); // callback for onGetPolyline()
void onFocus();
void onApplyNow();
void onUasChanged(const ParticipantManagement::UAS &uas);
void onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType);
void onMissionPlanChanged(const ParticipantManagement::MissionPlan &data, const DataModification modType);
private:
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
LtsSession::LtsSessionDataDds *m_session;
ParticipantManagement::Participant m_participantData;
ParticipantManagement::MissionPlan m_missionPlan;
int m_missionPlanId;
bool m_uiEnabled;
QSet<int> m_usedUasSet;
std::shared_ptr<SituationMap::ForeignGuiInterface> m_sitMapGuiIfc;
std::shared_ptr<ParticipantManagement::ForeignGuiComboboxInterface> m_ppmCbxGui;
LtsUtils::RequestHelper *m_requestHelper;
QMap<int, QString> m_uasCharges;
QTimer *m_participantTimer; // used when participant were updated
QPolygonF m_polyline;
ParticipantManagement::Participant m_participant;
bool m_participantReady { false };
LOGGER_T m_logger;
Ui::MissionPlanEdit *m_ui;
};
}
@@ -0,0 +1,353 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MissionPlanEdit</class>
<widget class="QWidget" name="MissionPlanEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>839</width>
<height>568</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="tableWidget">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="7" column="3">
<widget class="QLabel" name="lblParticipantState">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item row="5" column="2" rowspan="2">
<widget class="QLabel" name="lblUasParticipant">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>UAS Participant:</string>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QCheckBox" name="cbJumpToStart">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Jump To Start</string>
</property>
</widget>
</item>
<item row="1" column="1" rowspan="2" colspan="3">
<widget class="LtsWidgets::LtsDateTimeEdit" name="dteStartTime" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="3" rowspan="2">
<widget class="QComboBox" name="participantCBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblMoveStart">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start Time:</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="idleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Idle Time [s]:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelName">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="3" column="0" rowspan="2">
<widget class="QLabel" name="chargesLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Charge Type:</string>
</property>
</widget>
</item>
<item row="5" column="1" rowspan="2">
<widget class="QComboBox" name="cbParty"/>
</item>
<item row="0" column="7">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0" rowspan="2">
<widget class="QLabel" name="lblUASParty">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>UAS Units:</string>
</property>
</widget>
</item>
<item row="0" column="5" rowspan="2">
<widget class="QSpinBox" name="spIdleTime">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QCheckBox" name="cbRepeat">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Repeat</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QLabel" name="lblParticipantStateText">
<property name="text">
<string>Participant state:</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="nameLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="1" rowspan="2" colspan="3">
<widget class="QComboBox" name="chargesCBox"/>
</item>
<item row="5" column="4">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Edit Waypoints in Map:</string>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Prefix:</string>
</property>
</widget>
</item>
<item row="5" column="5">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
<property name="spacing">
<number>1</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QPushButton" name="pbPosition">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string/>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/position.svg</normaloff>:/style/position.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbClear">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string/>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/remove.svg</normaloff>:/style/remove.svg</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="5">
<widget class="QLineEdit" name="lePrefix">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maxLength">
<number>10</number>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Altitude</string>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QSpinBox" name="spAltitude">
<property name="maximum">
<number>120</number>
</property>
<property name="value">
<number>20</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsDateTimeEdit</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_date_time_edit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,235 @@
/**
* @RDE_HEADER@
* Dialog to edit drone Simulation
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
#include <LtsUtilsSettings/settings.h>
#include <ProjectDataManagement/pdm_translation.h>
#include "mission_plan_model.h"
namespace CentralEffectSimulation
{
/*!
* \brief MissionPlanModel::MissionPlanModel
* \param parent
*/
MissionPlanModel::MissionPlanModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<ParticipantManagement::MissionPlan>(
{tr("Name"),
tr("Mission Start"),
tr("Actions [fulfilled / Count]"),
tr("Fulfilled"),
tr("Participant"),
tr("UAS State"),
tr("Current Mission"),
tr("Aborted")},
parent)
, m_ppmDtc(nullptr)
, m_logger(getLogger("MissionPlanModel"))
{
}
/*!
* \brief MissionPlanModel::data
* \param index
* \param role
* \return
*/
QVariant MissionPlanModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole || !indexIsValid(index))
return QVariant();
const ParticipantManagement::MissionPlan &mp = m_vecData.at(index.row());
switch (index.column())
{
case Columns::Name:
return QString::fromStdString(mp.name());
case Columns::MissionStart:
return QDateTime::fromMSecsSinceEpoch(mp.missionStart());
case Columns::Fulfilled:
return QDateTime::fromMSecsSinceEpoch(mp.fulfilled());
case Columns::ItemsCountAndFulfilled:
{
int actionsFulfilled = 0;
int actionsCount = 0;
for (const auto &item : mp.items())
{
actionsFulfilled += item.actionsFulfilled();
actionsCount += item.actions().size();
}
return QStringLiteral("%1/%2").arg(actionsFulfilled).arg(actionsCount);// item fulfilled / item count;
}
case Columns::Participant:
{
const auto &participant = m_ppmDtc->participant(mp.participantId());
return QString::number(mp.participantId());//QString::fromStdString(participant.designator());
}
case Columns::UasState:
{
const auto &uas = m_ppmDtc->getUasByParticipantId(mp.participantId(), m_exerciseId);
return ProjectDataManagement::toString(uas.uasState());
}
case Columns::CurrentMission:
{
return currentJob2String(mp);
}
case Columns::Aborted:
{
return mp.aborted();
}
default:
break;
}
return QVariant();
}
/*!
* \brief MissionPlanModel::initModel
* \param ppmDtc
* \return
*/
bool MissionPlanModel::initModel(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, int exerciseId)
{
m_ppmDtc = ppmDtc;
m_ppmDtc->enableMissionPlan(true);
m_exerciseId = exerciseId;
if (!m_ppmDtc->isUASEnabled())
m_ppmDtc->enableUAS();
m_ppmDtc->waitForHistoricalDataUAS(1000);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::missionPlanChanged,
this, &MissionPlanModel::onMissionPlanChanged);
auto cb = [this](const ParticipantManagement::MissionPlan &msp)
{
onMissionPlanChanged(msp, DataModification::eAdded);
};
m_ppmDtc->missionPlans(cb);
return true;
}
QString MissionPlanModel::currentJob2String(const ParticipantManagement::MissionPlan &missionPlan) const
{
// get current action
// get latest actionType
ParticipantManagement::MissionAction currAction = m_ppmDtc->currentMissionAction(missionPlan.id());
if (currAction.planId() == 0 ||
static_cast<int>(missionPlan.items().size()) == missionPlan.itemsFulfilled())
return tr(" Mission Complete ");
switch (currAction.actionType().underlying())
{
case ParticipantManagement::MissionActionType::MoveTo:
{
return tr("Move to WP %1").arg(currAction.itemNumber()+1);
}
case ParticipantManagement::MissionActionType::Loiter:
{
return tr("WP %1: Loiter %2 sec.").arg(currAction.itemNumber()+1).arg(currAction.duration());
}
case ParticipantManagement::MissionActionType::Attack:
{
return tr("WP %1: Drop Charge").arg(currAction.itemNumber()+1);
}
default:
return tr("Unknown");
}
}
/*!
* \brief MissionPlanModel::onParticipantUASChanged
* \param data
* \param modType
*/
void MissionPlanModel::onMissionPlanChanged(const ParticipantManagement::MissionPlan &data, const DataModification modType)
{
const int id = data.id();
int row = findRowForId(id);
DEBUG_LOG(m_logger)<<"MISSION PLAN CHANGED id ( "<<data.id()<<" ) "<< modType;
switch (modType)
{
case DataModification::eModified:
{
if (row < 0)
return;
setRowData(row, data);
break;
}
case DataModification::eAdded:
{
if (row != -1)
return;
else
insertRow(data);
break;
}
case DataModification::eRemoved:
removeRow(row);
break;
}
}
/// #####################################
/// PARTICIPANT_UAS-POXYMODEL
/// #####################################
///
/*!
* \brief ParticipantUASProxyModel::setSourceModel
* \param sourceModel
*/
void MissionPlanProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<MissionPlanModel*>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
/*!
* \brief ParticipantUASProxyModel::setShowExpired
* \param show
*/
void MissionPlanProxyModel::setShowExpired(bool show)
{
m_showExpired = show;
}
/*!
* \brief ParticipantUASProxyModel::filterAcceptsRow
* \param source_row
* \param sourceParent
* \return
*/
bool MissionPlanProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
Q_UNUSED(sourceParent)
return checkFilter(sourceRow);
}
/*!
* \brief ParticipantUASProxyModel::checkFilter
* \param sourceRow
* \return
*/
bool MissionPlanProxyModel::checkFilter(int sourceRow) const
{
Q_UNUSED(sourceRow)
//const auto uas = m_sourceModel->getRowData(sourceRow);
return true;
}
}// namespace CentralEffectSimulation
@@ -0,0 +1,80 @@
/**
* @RDE_HEADER@
* Dialog to edit drone Simulation
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#pragma once
#include <QObject>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
//#include <CentralEffectSimulation/logger.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <ParticipantManagement/ppm.hpp>
#include "logger.h"
namespace CentralEffectSimulation
{
class MissionPlanModel : public LtsWidgets::LtsAbstractTableModel<ParticipantManagement::MissionPlan>
{
Q_OBJECT
public:
enum Columns
{
Name,
MissionStart,
ItemsCountAndFulfilled, // item Fulfilled / item count
Fulfilled,
Participant,
UasState,
CurrentMission,
Aborted
};
explicit MissionPlanModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override final;
bool initModel(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, int exerciseId);
QString currentJob2String(const ParticipantManagement::MissionPlan &missionPlan) const;
private slots:
void onMissionPlanChanged(const ParticipantManagement::MissionPlan &data, const DataModification modType);
private:
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
int m_exerciseId {-1};
LOGGER_T m_logger;
friend class EmergencyCallProxyModel;
};
/*!
* UASProxyModel
*/
class MissionPlanProxyModel : public LtsWidgets::LtsColumnFilterProxyModel
{
Q_OBJECT
public:
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
bool showExpired() const {return m_showExpired;}
void setSourceModel(QAbstractItemModel *sourceModel) override;
public slots:
void setShowExpired(bool show);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override final;
private:
bool checkFilter(int sourceRow)const;
private:
bool m_showExpired{false};
MissionPlanModel *m_sourceModel{nullptr};
};
} //namespace CentralEffectSimulation