358 lines
10 KiB
C++
Executable File
358 lines
10 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* AreaEffectSimulation - Contamination Edit Dialog
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Patric Karwat, SLSE, 2016-07-07
|
|
*/
|
|
|
|
|
|
#include "aes_base_edit.h"
|
|
|
|
#include <Plugins/AreaEffectSimulation/aes_data_plugin_interface.h>
|
|
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
|
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
|
|
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
|
|
#include <PluginInterfaces/gui_plugin_interface.h>
|
|
#include <LtsWidgets/lts_date_time_edit.h>
|
|
#include <LtsCoordTrans/lts_coord_widget.h>
|
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
|
#include <QSpinBox>
|
|
#include <QJsonDocument>
|
|
|
|
namespace AreaEffectSimulation
|
|
{
|
|
|
|
BaseEdit::BaseEdit(const QString &menuGroup, const QString &loggerName, QWidget *parent, LtsWidgets::ButtonCombination buttonCombination)
|
|
: LtsWidgets::LtsDialog(parent, buttonCombination)
|
|
, m_aesItem()
|
|
, m_dtc(nullptr)
|
|
, m_ftc(nullptr)
|
|
, m_sitMapGuiIfc(nullptr)
|
|
, m_ppmGuiCbxIfc(nullptr)
|
|
, m_session(nullptr)
|
|
, m_menuGroup(menuGroup)
|
|
, m_requestHelper(new LtsUtils::RequestHelper(this))
|
|
, m_logger(getLogger(loggerName))
|
|
{
|
|
m_aesItem.id(0);
|
|
connect(this, &BaseEdit::visibilityChanged, this, &BaseEdit::onVisibilityChanged);
|
|
}
|
|
|
|
void BaseEdit::setCombobxToValueIfPossible(QComboBox *cbx, int val)
|
|
{
|
|
int idx = cbx->findData(val);
|
|
if (idx >= 0)
|
|
cbx->setCurrentIndex(idx);
|
|
}
|
|
|
|
void BaseEdit::onAesItemCreateReply(const Core::FunctionReplyObject &replyObject)
|
|
{
|
|
m_requestHelper->requestReply(replyObject);
|
|
if (!replyObject.error() && m_sitMapGuiIfc)
|
|
m_sitMapGuiIfc->removePreviewObject();
|
|
}
|
|
|
|
void BaseEdit::onAesItemModifyReply(const Core::FunctionReplyObject &replyObject)
|
|
{
|
|
m_requestHelper->requestReply(replyObject);
|
|
|
|
if (!replyObject.error() && m_sitMapGuiIfc )
|
|
m_sitMapGuiIfc->removePreviewObject();
|
|
}
|
|
|
|
bool BaseEdit::isModifiable() const
|
|
{
|
|
AesEffectState effectState = m_aesItem.effectStateId();
|
|
return (effectState == AreaEffectSimulation::AesEffectState_def::EsPaused ||
|
|
effectState == AreaEffectSimulation::AesEffectState_def::EsActivated ||
|
|
effectState == AreaEffectSimulation::AesEffectState_def::EsPrepared ||
|
|
effectState == AreaEffectSimulation::AesEffectState_def::EsCreated ||
|
|
effectState == AreaEffectSimulation::AesEffectState_def::EsUnknown ||
|
|
effectState == AreaEffectSimulation::AesEffectState_def::EsReceived) &&
|
|
(!m_session->isReplayPartition()) &&
|
|
(m_session->app() != LtsAppDescriptor::App::OC); // not OC-App
|
|
}
|
|
|
|
bool BaseEdit::isOnlyNameModifiable() const
|
|
{
|
|
AesEffectState effectState = m_aesItem.effectStateId();
|
|
return (effectState == AreaEffectSimulation::AesEffectState_def::EsPaused ||
|
|
effectState == AreaEffectSimulation::AesEffectState_def::EsActivated) &&
|
|
(!m_session->isReplayPartition()) &&
|
|
(m_session->app() != LtsAppDescriptor::App::OC); // not OC-App
|
|
}
|
|
|
|
void BaseEdit::correctSpinBoxValue()
|
|
{
|
|
QSpinBox *sb = qobject_cast<QSpinBox*>(sender());
|
|
if (sb)
|
|
return correctSpinBoxValueInternal(sb);
|
|
|
|
QDoubleSpinBox *dsb = qobject_cast<QDoubleSpinBox*>(sender());
|
|
if (dsb)
|
|
return correctSpinBoxValueInternal(dsb);
|
|
}
|
|
|
|
void BaseEdit::onVisibilityChanged(bool visible)
|
|
{
|
|
if (!m_sitMapGuiIfc)
|
|
return;
|
|
|
|
if (!visible)
|
|
m_sitMapGuiIfc->removePreviewObject();
|
|
else
|
|
showPreviewObject();
|
|
}
|
|
|
|
void BaseEdit::correctSpinBoxValueInternal(QSpinBox *sb)
|
|
{
|
|
if (!sb)
|
|
return;
|
|
|
|
int step = sb->singleStep();
|
|
if (step == 1)
|
|
return;
|
|
|
|
int value = sb->value();
|
|
int remainder = (value - sb->minimum()) % step;
|
|
if (remainder == 0)
|
|
return;
|
|
|
|
if (2 * remainder > step)
|
|
value += (step - remainder);
|
|
else
|
|
value -= remainder;
|
|
|
|
if (value > sb->maximum())
|
|
value -= step;
|
|
|
|
if(value < sb->minimum())
|
|
value = sb->minimum();
|
|
|
|
sb->setValue(value);
|
|
}
|
|
|
|
void BaseEdit::correctSpinBoxValueInternal(QDoubleSpinBox *sb)
|
|
{
|
|
if (!sb)
|
|
return;
|
|
|
|
double step = sb->singleStep();
|
|
if (qFuzzyCompare(step, pow(10, sb->decimals())))
|
|
return;
|
|
|
|
double value = sb->value();
|
|
double remainder = fmod(value - sb->minimum(), step);
|
|
if (qFuzzyCompare(remainder, 0))
|
|
return;
|
|
|
|
if (2 * remainder > step)
|
|
value += (step - remainder);
|
|
else
|
|
value -= remainder;
|
|
|
|
if (value > sb->maximum())
|
|
value -= step;
|
|
|
|
if (value < sb->minimum())
|
|
value = sb->minimum();
|
|
|
|
sb->setValue(value);
|
|
}
|
|
|
|
bool BaseEdit::hasValidEffectStart(LtsWidgets::LtsDateTimeEdit *dte) const
|
|
{
|
|
if (isOnlyNameModifiable())
|
|
return true;
|
|
// if there is visible a enabled/disable checkbox -> also allow 0 as effect start
|
|
if (dte->isCheckable() && !dte->isChecked())
|
|
return true;
|
|
const auto ms = dte->msecsSinceEpoch();
|
|
return ms > m_session->partitionTime();
|
|
}
|
|
|
|
void BaseEdit::setEffectStartEnabled(LtsWidgets::LtsDateTimeEdit *dte, bool enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
int exerciseId = m_session->exerciseId();
|
|
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
|
|
m_session->getDtc<ExerciseManagement::DataInterface>(exmDtc);
|
|
ExerciseManagement::Exercise exercise = exmDtc->exercise(exerciseId);
|
|
|
|
// Enablen der gesamten Checkbox nur, wenn Übung läuft:
|
|
enable = (exercise.state() == ExerciseManagement::ExerciseState_def::Started ||
|
|
exercise.state() == ExerciseManagement::ExerciseState_def::Paused);
|
|
|
|
// In Übungsvorbereitung keine Eingabe einer Zeit möglich:
|
|
if (exercise.state() == ExerciseManagement::ExerciseState_def::Created)
|
|
dte->setChecked(false);
|
|
else // Ansonsten Status gemäß eingetragener Zeit.
|
|
dte->setChecked(dte->msecsSinceEpoch() > 0);
|
|
}
|
|
else // Ansonsten Status gemäß eingetragener Zeit.
|
|
dte->setChecked(dte->msecsSinceEpoch() > 0);
|
|
// Gesamtstatus übernehmen.
|
|
dte->setEnabled(enable);
|
|
}
|
|
|
|
bool BaseEdit::has2DMap(bool showMsgBoxIfNot2D)
|
|
{
|
|
if (!m_sitMapGuiIfc)
|
|
return false;
|
|
if (m_sitMapGuiIfc->mapMode() != SituationMap::MapMode::MapMode_2D)
|
|
{
|
|
if (showMsgBoxIfNot2D)
|
|
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Only available in 2D mode"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void BaseEdit::setCoordinates(const QVector<QPointF> &coords, bool reverse)
|
|
{
|
|
// Reverse if Frontline is chosen and width is taken to the right (like drawing an L)
|
|
clearCoordinates();
|
|
const auto cWidgets(coordWidgets());
|
|
if (!reverse)
|
|
{
|
|
for (size_t i = 0; i < cWidgets.size() && i < static_cast<size_t>(coords.size()); ++i)
|
|
cWidgets.at(i)->setCoordinate(coords.at(i));
|
|
}
|
|
else
|
|
{
|
|
auto copy(coords);
|
|
std::reverse(std::begin(copy), std::end(copy));
|
|
for (size_t i = 0; i < cWidgets.size() && i < static_cast<size_t>(copy.size()); ++i)
|
|
cWidgets.at(i)->setCoordinate(copy.at(i));
|
|
}
|
|
}
|
|
|
|
void BaseEdit::setCoordinates(const std::vector<Core::GeoPosition2D> &coords)
|
|
{
|
|
clearCoordinates();
|
|
const auto cWidgets(coordWidgets());
|
|
for (size_t i = 0; i < cWidgets.size() && i < static_cast<size_t>(coords.size()); ++i)
|
|
cWidgets.at(i)->setCoordinate(coords.at(i).lon(), coords.at(i).lat());
|
|
}
|
|
|
|
std::vector<Core::GeoPosition2D> BaseEdit::getCoordinates() const
|
|
{
|
|
std::vector<Core::GeoPosition2D> segments;
|
|
const auto cWidgets(coordWidgets());
|
|
segments.reserve(cWidgets.size());
|
|
for (const auto cw : cWidgets)
|
|
{
|
|
const QPointF coord = cw->coordinate();
|
|
if (coord.isNull())
|
|
break;
|
|
segments.emplace_back(Core::GeoPosition2D(coord.x(), coord.y()));
|
|
}
|
|
return segments;
|
|
}
|
|
|
|
QVector<QPointF> BaseEdit::getCoordinatesQPointF() const
|
|
{
|
|
QVector<QPointF> segments;
|
|
const auto cWidgets(coordWidgets());
|
|
segments.reserve(cWidgets.size());
|
|
for (const auto *cw : cWidgets)
|
|
{
|
|
const QPointF coord = cw->coordinate();
|
|
if (coord.isNull())
|
|
break;
|
|
segments.push_back(coord);
|
|
}
|
|
return segments;
|
|
}
|
|
|
|
void BaseEdit::clearCoordinates()
|
|
{
|
|
for (auto *cw : coordWidgets())
|
|
cw->clear();
|
|
}
|
|
|
|
void BaseEdit::enabledCoordinates(bool enable)
|
|
{
|
|
for (auto *cw : coordWidgets())
|
|
cw->setEnabled(enable);
|
|
}
|
|
|
|
|
|
void BaseEdit::onAreaChanged(const AesItem &data, DataModification modType)
|
|
{
|
|
// No, we don't update the UI elements (yet). If we wanted to, we had to
|
|
// implement a virtual method such as enableUiElements...
|
|
if (data.id() != m_aesItem.id())
|
|
return;
|
|
|
|
if (modType == DataModification::eRemoved || data.effectStateId() == AesEffectState_def::EsRemoved)
|
|
{
|
|
// As a simple 'close()' does not work:
|
|
__unchange__();
|
|
closeRequest();
|
|
return;
|
|
}
|
|
m_aesItem = data;
|
|
enableUiElements(isModifiable() && !isOnlyNameModifiable());
|
|
}
|
|
|
|
bool BaseEdit::init(int aesItemId, LtsSession::LtsSession *session)
|
|
{
|
|
if (m_session)
|
|
{
|
|
ERROR_LOG(m_logger) << "double init()!";
|
|
return false;
|
|
}
|
|
m_session = session;
|
|
m_aesItem.id(aesItemId);
|
|
|
|
if (!m_session->getDtc<AreaEffectSimulation::DataInterface>(m_dtc))
|
|
return false;
|
|
if (!m_session->getFtc<AreaEffectSimulation::DataInterface>(m_ftc))
|
|
return false;
|
|
m_session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(m_sitMapGuiIfc);
|
|
if (!m_session->getForeignGuiInterface<ParticipantManagement::ForeignGuiComboboxInterface>(m_ppmGuiCbxIfc))
|
|
return false;
|
|
|
|
if (m_session->isReplayPartition() || (LtsAppDescriptor::App::OC == m_session->app()))
|
|
setUnchangeable();
|
|
|
|
if (aesItemId == 0)
|
|
{
|
|
m_aesItem.effectSourceId(ProjectDataManagement::EffectSource_def::ES_Internal);
|
|
m_buttonBox->setText(LtsWidgets::DialogButton::Ok, tr("Add"));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
double BaseEdit::distance(const Core::GeoPosition2D &p1, const Core::GeoPosition2D &p2)
|
|
{
|
|
LtsCoordTrans::UtmCoordinate start = LtsCoordTrans::UtmCoordinate::fromGeographic(p1.lon(), p1.lat());
|
|
LtsCoordTrans::UtmCoordinate end = LtsCoordTrans::UtmCoordinate::fromGeographic(p2.lon(), p2.lat(), start.zone);
|
|
double dx = start.x - end.x;
|
|
double dy = start.y - end.y;
|
|
return std::sqrt(dx*dx + dy*dy);
|
|
}
|
|
|
|
bool BaseEdit::checkSegementLengths(QWidget *parent) const
|
|
{
|
|
auto segments = getCoordinates();
|
|
for (auto i=0; i<((int) segments.size() - 1); ++i)
|
|
{
|
|
double dis = distance(segments[i], segments[i+1]);
|
|
if (getMaxLength() != 0 && dis > getMaxLength())
|
|
{
|
|
LtsWidgets::LtsMessageDialog::showMessage(parent, tr("The distance between Coordinate %1 and %2 is too long.\nThe segment is %3m long, but the maximum segment length is %4m.")
|
|
.arg(i+1).arg(i+2).arg(QString::number(dis,'d',2),QString::number(getMaxLength(),'d',2)));
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace AreaEffectSimulation
|