830 lines
29 KiB
C++
Executable File
830 lines
29 KiB
C++
Executable File
/**
|
|
* @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
|