Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
# CMakeLists.txt fuer DdsView
|
||||
#
|
||||
|
||||
SET(ddsview_SRCS
|
||||
detailview.cpp
|
||||
detailviewdlg.cpp
|
||||
detailviewdlg.h
|
||||
detailview.h
|
||||
initialisation.cpp
|
||||
initialisation.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
nglsettings.cpp
|
||||
nglsettings.h
|
||||
sequences/abstractsequencewidget.cpp
|
||||
sequences/abstractsequencewidget.h
|
||||
sequences/tabsequencewidget.cpp
|
||||
sequences/tabsequencewidget.h
|
||||
sequences/listsequencewidget.cpp
|
||||
sequences/listsequencewidget.h
|
||||
samplemodel.cpp
|
||||
samplemodel.h
|
||||
sampleview.cpp
|
||||
sampleview.h
|
||||
subscriber.cpp
|
||||
subscriber.h
|
||||
telegrammodel.cpp
|
||||
telegrammodel.h
|
||||
telegramview.cpp
|
||||
telegramview.h
|
||||
topicselector.cpp
|
||||
topicselector.h
|
||||
topic_tree_model.cpp
|
||||
topic_tree_model.h
|
||||
typecodeselector.cpp
|
||||
typecodeselectordlg.cpp
|
||||
typecodeselectordlg.h
|
||||
typecodeselector.h
|
||||
xmlhelper.cpp
|
||||
xmlhelper.h
|
||||
zwmsui_version.cpp
|
||||
zwmsui_version.h
|
||||
rw/rwwidgets.cpp
|
||||
rw/rwwidgets.h
|
||||
coorddetaildlg.cpp
|
||||
coorddetaildlg.h
|
||||
)
|
||||
|
||||
SET(ddsview_UICS
|
||||
ui/mainwindow.ui
|
||||
ui/zwmsui_versionview.ui
|
||||
ui/detailviewdlg.ui
|
||||
ui/typecodeselectordlg.ui
|
||||
ui/topicselector.ui
|
||||
ui/coorddetaildlg.ui
|
||||
)
|
||||
|
||||
NGL_WRAP_UI(ddsview_SRCS ${ddsview_UICS})
|
||||
NGL_ADD_TOOL_ICON(ddsview_SRCS "dds")
|
||||
|
||||
NGL_ADD_EXECUTABLE(ddsview TOOLSWS TOOLSSV NO_TRANSLATION WIN32 ${ddsview_SRCS} ${ddsview_UICS})
|
||||
NGL_TARGET_LINK_LIBRARIES(ddsview
|
||||
ddsconnector
|
||||
LtsCommandLineParser
|
||||
LtsCoordTrans
|
||||
ltsCoordWidget
|
||||
LtsWidgets
|
||||
LtsPluginLoader
|
||||
nglapplication
|
||||
NGLLogger
|
||||
nglsettings
|
||||
nglwidgets
|
||||
Qt5::Core
|
||||
Qt5::Gui
|
||||
Qt5::Xml
|
||||
RTI::nddsc
|
||||
RTI::nddscore
|
||||
topicdiscover
|
||||
topic_type_plugin
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(ddsview)
|
||||
NGL_INSTALL(TOOLSWS TOOLSSV FILES log4cxx_ddsview.props DESTINATION "bin")
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Helper dia,log to enter coordinates as MGRS
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "coorddetaildlg.h"
|
||||
#include "ui_coorddetaildlg.h"
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
#include <LtsCoordTrans/lts_coord_widget.h>
|
||||
#include <QPushButton>
|
||||
|
||||
CoordDetailDlg::CoordDetailDlg(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_ui(new Ui::CoordDetailDlg)
|
||||
{
|
||||
// prefill some mgrs squares
|
||||
LtsCoordTrans::setDefaultMgrsSquares({QStringLiteral("32UMD"), QStringLiteral("32UND"), QStringLiteral("32UPD"), QStringLiteral("32UPC")});
|
||||
|
||||
m_ui->setupUi(this);
|
||||
connect(m_ui->buttonBox, &QDialogButtonBox::rejected,
|
||||
this, &CoordDetailDlg::reject);
|
||||
connect(m_ui->buttonBox, &QDialogButtonBox::accepted,
|
||||
this, &CoordDetailDlg::accept);
|
||||
connect(m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::coordinateChanged,
|
||||
this, &CoordDetailDlg::changedValues);
|
||||
connect(m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::invalidCoordinate,
|
||||
this, &CoordDetailDlg::invalidCoordinate);
|
||||
m_ui->positionEdit->setMgrsSquareEditable(true);
|
||||
}
|
||||
|
||||
CoordDetailDlg::~CoordDetailDlg()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void CoordDetailDlg::setValues(double lon, double lat)
|
||||
{
|
||||
m_ui->positionEdit->setCoordinate(lon, lat, true);
|
||||
changedValues(QPointF(lon, lat));
|
||||
}
|
||||
|
||||
QPointF CoordDetailDlg::coordinate() const
|
||||
{
|
||||
return m_ui->positionEdit->coordinate();
|
||||
}
|
||||
|
||||
void CoordDetailDlg::changedValues(const QPointF &coord)
|
||||
{
|
||||
m_ui->lonText->setText(QString::number(coord.x()));
|
||||
m_ui->latText->setText(QString::number(coord.y()));
|
||||
enableOkButton(true);
|
||||
}
|
||||
|
||||
void CoordDetailDlg::invalidCoordinate()
|
||||
{
|
||||
enableOkButton(false);
|
||||
}
|
||||
|
||||
void CoordDetailDlg::enableOkButton(bool bEnable)
|
||||
{
|
||||
QPushButton *pb = m_ui->buttonBox->button(QDialogButtonBox::Ok);
|
||||
if (!pb)
|
||||
return;
|
||||
pb->setEnabled(bEnable);
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Helper dia,log to enter coordinates as MGRS
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class CoordDetailDlg;
|
||||
}
|
||||
|
||||
class CoordDetailDlg : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CoordDetailDlg(QWidget *parent = nullptr);
|
||||
~CoordDetailDlg();
|
||||
|
||||
void setValues(double lon, double lat);
|
||||
QPointF coordinate() const;
|
||||
|
||||
private slots:
|
||||
void changedValues(const QPointF &coord);
|
||||
void invalidCoordinate();
|
||||
private:
|
||||
void enableOkButton(bool bEnable);
|
||||
private:
|
||||
Ui::CoordDetailDlg *m_ui;
|
||||
};
|
||||
Executable
+784
@@ -0,0 +1,784 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Widget to dynamically show dds::core::xtypes::DynamicData.
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "detailview.h"
|
||||
#include "rw/rwwidgets.h"
|
||||
|
||||
#include "sequences/tabsequencewidget.h"
|
||||
#include "sequences/listsequencewidget.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QtCore/QMap>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTabWidget>
|
||||
#include <QPushButton>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <limits>
|
||||
#include <functional>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
|
||||
#include "coorddetaildlg.h"
|
||||
#include "topic_tree_model.h"
|
||||
|
||||
DetailView::DetailView(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_type(dds::core::xtypes::StructType("Unknown"))
|
||||
, m_readonly(RwMode::ReadWrite)
|
||||
, m_logger(DdsView::getLogger("DetailView"))
|
||||
{
|
||||
}
|
||||
|
||||
DetailView::~DetailView()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool DetailView::init(const dds::core::xtypes::DynamicData &data, const QString &topicName, RwMode rwMode)
|
||||
{
|
||||
// Already initialized or no valid data...
|
||||
if (!init(data.type(), topicName, rwMode))
|
||||
return false;
|
||||
|
||||
return setData(data);
|
||||
}
|
||||
|
||||
bool DetailView::init(const dds::core::xtypes::DynamicType &type, const QString &topicName, RwMode rwMode)
|
||||
{
|
||||
if (type.kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
return false;
|
||||
|
||||
m_readonly = rwMode;
|
||||
|
||||
if (m_type == type)
|
||||
return true;
|
||||
|
||||
clear();
|
||||
|
||||
m_type = type;
|
||||
|
||||
// Data from a specific Topic
|
||||
if (topicName.isEmpty())
|
||||
m_topicName = QString::fromStdString(type.name());
|
||||
else
|
||||
m_topicName = topicName;
|
||||
|
||||
setWindowTitle(m_topicName);
|
||||
|
||||
return generateUi(m_readonly, m_type, this, QString(), m_logger);
|
||||
}
|
||||
|
||||
void DetailView::clear()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Clear";
|
||||
while (QWidget *w = findChild<QWidget *>())
|
||||
delete w;
|
||||
|
||||
delete layout();
|
||||
m_type = dds::core::xtypes::StructType("Unknown");
|
||||
m_topicName.clear();
|
||||
}
|
||||
|
||||
|
||||
bool DetailView::generateUi(const RwMode &readOnly,
|
||||
const dds::core::xtypes::DynamicType &type,
|
||||
QWidget *parentWidget,
|
||||
const QString &name,
|
||||
NGL::INGLLogger &logger,
|
||||
QGridLayout *grid)
|
||||
{
|
||||
if (type.kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
return false;
|
||||
|
||||
const dds::core::xtypes::StructType &structType = static_cast<const dds::core::xtypes::StructType &>(type);
|
||||
|
||||
QGridLayout *gl = grid ? grid : new QGridLayout(parentWidget);
|
||||
//DEBUG_LOG(m_logger) << "generateUi(" << structType.name() << ")";
|
||||
|
||||
int row = gl->rowCount();
|
||||
dds::core::xtypes::StructType typeCurrent = structType;
|
||||
bool hasBaseClass = true;
|
||||
|
||||
while (hasBaseClass)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (const dds::core::xtypes::Member &member : typeCurrent.members())
|
||||
{
|
||||
QString memberName;
|
||||
if (name.length())
|
||||
memberName += name + QLatin1Char('.');
|
||||
memberName += QString::fromStdString(member.name());
|
||||
|
||||
QWidget *w = createWidget(readOnly, member.type(), memberName, logger);
|
||||
w->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
|
||||
if (member.type().kind() == dds::core::xtypes::TypeKind::STRUCTURE_TYPE || // no label for groupboxes (which already contain a label)
|
||||
member.type().kind() == dds::core::xtypes::TypeKind::SEQUENCE_TYPE)
|
||||
gl->addWidget(w, row, 0, 1, 2);
|
||||
else
|
||||
{
|
||||
QLabel *newLabel = new QLabel(QString::fromStdString(member.name()));
|
||||
|
||||
newLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
|
||||
|
||||
gl->addWidget(newLabel, row, 0, 1, 1);
|
||||
gl->addWidget(w, row, 1, 1, 1);
|
||||
}
|
||||
++row;
|
||||
}
|
||||
if (typeCurrent.has_parent())
|
||||
typeCurrent = typeCurrent.parent();
|
||||
else
|
||||
hasBaseClass = false;
|
||||
}
|
||||
catch (const dds::core::Exception &)
|
||||
{
|
||||
// finished
|
||||
}
|
||||
}
|
||||
|
||||
gl->addItem(new QSpacerItem(20, 4, QSizePolicy::Minimum, QSizePolicy::Expanding), row, 0, 1, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DetailView::setWidgetData(const dds::core::xtypes::DynamicData &data,
|
||||
const dds::core::xtypes::TypeKind &typeKind,
|
||||
uint32_t index,
|
||||
const QString &name,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger)
|
||||
{
|
||||
DEBUG_LOG(logger) << "setWidgetData for " << name;
|
||||
|
||||
if (!data.member_exists(index))
|
||||
{
|
||||
DEBUG_LOG(logger)
|
||||
<< "Member " << name << "is optional and does not exist";
|
||||
return true;
|
||||
}
|
||||
switch (typeKind.underlying()) //info.member_kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWSpinBox *>(name), data.value<char>(index));
|
||||
break;
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
case dds::core::xtypes::TypeKind::CHAR_32_TYPE:
|
||||
#else
|
||||
case dds::core::xtypes::TypeKind::CHAR_16_TYPE:
|
||||
#endif
|
||||
setSpinBoxValue(useOn->findChild<RWSpinBox *>(name), data.value<DDS_Wchar>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
setBitfieldValue(useOn->findChild<RWBitfieldWidget*>(name),data.value<uint8_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWSpinBox *>(name), data.value<int16_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWSpinBox *>(name), data.value<uint16_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWSpinBox *>(name), data.value<int32_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWDoubleSpinBox *>(name), data.value<uint32_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWDoubleSpinBox *>(name), data.value<int64_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWDoubleSpinBox *>(name), data.value<uint64_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWDoubleSpinBox *>(name), data.value<float>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
setSpinBoxValue(useOn->findChild<RWDoubleSpinBox *>(name), data.value<double>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
setComboBoxValue(useOn->findChild<RWComboBox *>(name), data.value<int32_t>(index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
{
|
||||
RWCheckBox *w = useOn->findChild<RWCheckBox *>(name);
|
||||
if (!w)
|
||||
{
|
||||
WARNING_LOG(logger) << "setData: (BOOLEAN_TYPE) - can not find checkbox with name " << name;
|
||||
break;
|
||||
}
|
||||
|
||||
DDS_Boolean val = data.value<bool>(index);
|
||||
w->setChecked(val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
RWLineEdit *w = useOn->findChild<RWLineEdit *>(name);
|
||||
if (!w)
|
||||
{
|
||||
WARNING_LOG(logger) << "setData: (BOOLEAN_TYPE) - can not find lineedit with name " << name;
|
||||
break;
|
||||
}
|
||||
|
||||
const dds::core::string tmp = data.value<dds::core::string>(index);
|
||||
if (tmp.c_str()) // unset -> nullptr
|
||||
w->setText(QString::fromUtf8(tmp.c_str()));
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE:
|
||||
{
|
||||
if (!useOn->findChild<QWidget *>(name))
|
||||
{
|
||||
WARNING_LOG(logger) << "setData: (STRUCTURE_TYPE) - can not find groupbox/widget with name " << name;
|
||||
break;
|
||||
}
|
||||
|
||||
const dds::core::xtypes::DynamicData inner = data.value<dds::core::xtypes::DynamicData>(index);
|
||||
const dds::core::xtypes::StructType &innerType = static_cast<const dds::core::xtypes::StructType &>(inner.type());
|
||||
dds::core::xtypes::StructType innerTypeCopy = innerType;
|
||||
|
||||
for (uint32_t inner_index = 1; inner_index <= inner.member_count(); ++inner_index)
|
||||
{
|
||||
const dds::core::xtypes::Member &member = TopicDataModel::memberFromType(innerType, innerTypeCopy, inner_index - 1);
|
||||
|
||||
QString memberName;
|
||||
if (name.length())
|
||||
memberName += name + QLatin1Char('.');
|
||||
memberName += QString::fromStdString(member.name());
|
||||
|
||||
setWidgetData(inner, member.type().kind(), inner_index, memberName, useOn, logger);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
{
|
||||
AbstractSequenceWidget *seq = useOn->findChild<AbstractSequenceWidget*>(name);
|
||||
if (!seq)
|
||||
{
|
||||
WARNING_LOG(logger) << "setData: (SEQUENCE_TYPE) - can not find primitive sequence with name " << name;
|
||||
QLabel *lab = useOn->findChild<QLabel *>(QLatin1String("LabelMcLabFace"));
|
||||
if (!lab)
|
||||
break;
|
||||
lab->setText(tr("Pssst. I can at least tell you,\nthere are %1 elements in that sequence!").arg(data.value<dds::core::xtypes::DynamicData>(index).member_count()));
|
||||
break;
|
||||
}
|
||||
|
||||
const dds::core::xtypes::DynamicData inner = data.value<dds::core::xtypes::DynamicData>(index);
|
||||
|
||||
seq->setMemberCount(inner.member_count());
|
||||
for (uint32_t inner_index = 1; inner_index <= inner.member_count(); ++inner_index)
|
||||
seq->setValue(inner, inner_index, useOn); // DAS MUSS PRIMITIVE SEQUENCE SELBER AUFRUFEN - METHODE MUSS ALSO VON KLASSE GETRENNT WERDEN: setWidgetData(inner, seqType.kind(), inner_index, name + QLatin1String(".") + QString::number(inner_index));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
WARNING_LOG(logger) << "setData: (" << QString::number(data.type_kind().underlying(), 16) << ") - no handler!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DetailView::setData(const dds::core::xtypes::DynamicData &data, const QString &name)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "setData for " << name;
|
||||
|
||||
// Not initialized or no valid data... <= heisst ?
|
||||
if (m_type.kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
return false;
|
||||
|
||||
for (uint32_t member_index = 1; member_index <= data.member_count(); ++member_index)
|
||||
{
|
||||
if (!data.member_exists(member_index))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "member " << name << "." << member_index << " does not exist - optional.";
|
||||
continue;
|
||||
}
|
||||
|
||||
const rti::core::xtypes::DynamicDataMemberInfo info = data.member_info(member_index);
|
||||
QString memberName;
|
||||
if (name.length())
|
||||
memberName += name + QLatin1Char('.');
|
||||
memberName += QString::fromStdString(info.member_name());
|
||||
|
||||
setWidgetData(data, info.member_kind(), member_index, memberName, this, m_logger);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
void DetailView::getSpinBoxValue(const QString &name,
|
||||
dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger)
|
||||
{
|
||||
S *w = useOn->findChild<S *>(name);
|
||||
|
||||
if (!w)
|
||||
{
|
||||
WARNING_LOG(logger) << "DetailView::getSpinBoxValue: Could not find widget with name '"
|
||||
<< name << "'";
|
||||
return;
|
||||
}
|
||||
|
||||
const T val = w->value();
|
||||
data.value<T>(member_index, val);
|
||||
}
|
||||
|
||||
void DetailView::getComboBoxValue(const QString &name,
|
||||
dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger)
|
||||
{
|
||||
RWComboBox *w = useOn->findChild<RWComboBox *>(name);
|
||||
|
||||
if (!w)
|
||||
{
|
||||
WARNING_LOG(logger) << "DetailView::getComboBoxValue: Could not find widget with name '"
|
||||
<< name << "'";
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t val = w->currentValue();
|
||||
|
||||
data.value<int32_t>(member_index, val);
|
||||
}
|
||||
|
||||
template <typename T, typename B>
|
||||
void DetailView::getBitfieldValue(const QString &name,
|
||||
dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger)
|
||||
{
|
||||
B *w = useOn->findChild<B *>(name);
|
||||
|
||||
if (!w)
|
||||
{
|
||||
WARNING_LOG(logger) << "DetailView::getBitfieldValue: Could not find widget with name '"
|
||||
<< name << "'";
|
||||
return;
|
||||
}
|
||||
|
||||
const T val = w->value();
|
||||
data.value<T>(member_index, val);
|
||||
}
|
||||
|
||||
|
||||
void DetailView::getWidgetData(dds::core::xtypes::DynamicData &data,
|
||||
const dds::core::xtypes::TypeKind &typeKind,
|
||||
uint32_t index,
|
||||
const QString &name,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger)
|
||||
{
|
||||
switch (typeKind.underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
getSpinBoxValue<char, RWSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
getBitfieldValue<uint8_t, RWBitfieldWidget>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
getSpinBoxValue<int16_t, RWSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
getSpinBoxValue<uint16_t, RWSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
getSpinBoxValue<int32_t, RWSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
getSpinBoxValue<uint32_t, RWDoubleSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
getSpinBoxValue<int64_t, RWDoubleSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
getSpinBoxValue<uint64_t, RWDoubleSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
getSpinBoxValue<float, RWDoubleSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
getSpinBoxValue<double, RWDoubleSpinBox>(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
getComboBoxValue(name, data, index, useOn, logger);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
{
|
||||
RWCheckBox *w = useOn->findChild<RWCheckBox *>(name);
|
||||
if (!w)
|
||||
{
|
||||
WARNING_LOG(logger) << "getData: (BOOLEAN_TYPE) - can not find checkbox with name " << name;
|
||||
break;
|
||||
}
|
||||
|
||||
data.value<bool>(index, w->isChecked());
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
RWLineEdit *w = useOn->findChild<RWLineEdit *>(name);
|
||||
if (!w)
|
||||
{
|
||||
WARNING_LOG(logger) << "getData: (STRING_TYPE) - can not find lineedit with name " << name;
|
||||
break;
|
||||
}
|
||||
|
||||
data.value<std::string>(index, w->text().toStdString());
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE: // top level is always structure type ! <-- ...but top level wont get called here -jh
|
||||
{
|
||||
rti::core::xtypes::LoanedDynamicData innerLoaned = data.loan_value(index);
|
||||
dds::core::xtypes::DynamicData &inner = innerLoaned.get();
|
||||
|
||||
const dds::core::xtypes::StructType &innerType = static_cast<const dds::core::xtypes::StructType &>(inner.type());
|
||||
dds::core::xtypes::StructType innerTypeCopy = innerType;
|
||||
|
||||
for(uint32_t member_index = 1; member_index <= inner.member_count(); ++member_index)
|
||||
{
|
||||
const dds::core::xtypes::Member &member = TopicDataModel::memberFromType(innerType, innerTypeCopy, member_index -1);
|
||||
|
||||
QString findObjectName = name + QLatin1Char('.') + QString::fromStdString(member.name());
|
||||
|
||||
DEBUG_LOG(logger) << "structure " << findObjectName;
|
||||
|
||||
getWidgetData(inner, member.type().kind(), member_index, findObjectName, useOn, logger);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
{
|
||||
AbstractSequenceWidget *seq = useOn->findChild<AbstractSequenceWidget*>(name);
|
||||
if (!seq)
|
||||
{
|
||||
WARNING_LOG(logger) << "getData: (SEQUENCE_TYPE) - can not find primitive sequence with name " << name;
|
||||
break;
|
||||
}
|
||||
|
||||
rti::core::xtypes::LoanedDynamicData innerLoaned = data.loan_value(index);
|
||||
dds::core::xtypes::DynamicData &inner = innerLoaned.get();
|
||||
|
||||
|
||||
for (uint32_t member_index = 1; member_index <= seq->getMemberCount(); ++member_index)
|
||||
{
|
||||
seq->getValue(name, inner, member_index, useOn);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
WARNING_LOG(logger) << "getData: (" << QString::number(data.type_kind().underlying(), 16) << ") - no handler!";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QSharedPointer<dds::core::xtypes::DynamicData> DetailView::getData() const
|
||||
{
|
||||
QSharedPointer<dds::core::xtypes::DynamicData> data(new dds::core::xtypes::DynamicData(m_type));
|
||||
|
||||
// data[n] runs from 1..count()!
|
||||
const dds::core::xtypes::StructType &innerType = static_cast<const dds::core::xtypes::StructType &>(data->type());
|
||||
dds::core::xtypes::StructType innerTypeCopy = innerType;
|
||||
|
||||
for (uint32_t member_index = 1; member_index <= data->member_count(); ++member_index)
|
||||
{
|
||||
const dds::core::xtypes::Member &member = TopicDataModel::memberFromType(innerType, innerTypeCopy, member_index - 1);
|
||||
getWidgetData(*data, member.type().kind(), member_index, QString::fromStdString(member.name()), this, m_logger);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
void DetailView::setSpinBoxValue(S *b, const T &value)
|
||||
{
|
||||
Q_ASSERT(b);
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
b->setMinimum(std::numeric_limits<T>::lowest());
|
||||
b->setMaximum(std::numeric_limits<T>::max());
|
||||
b->setValue(value);
|
||||
}
|
||||
|
||||
|
||||
void DetailView::setComboBoxValue(RWComboBox *w, int ordinal)
|
||||
{
|
||||
Q_ASSERT(w);
|
||||
if (!w)
|
||||
return;
|
||||
|
||||
w->setCurrentValue(ordinal);
|
||||
}
|
||||
|
||||
template <typename T, typename B>
|
||||
void DetailView::setBitfieldValue(B *b, const T &value)
|
||||
{
|
||||
Q_ASSERT(b);
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
b->setValue(value);
|
||||
}
|
||||
|
||||
static QWidget *createSpinBox(const RwMode &readOnly, QWidget *parent, int min, int max)
|
||||
{
|
||||
RWSpinBox *w = new RWSpinBox(readOnly, parent);
|
||||
w->setMinimum(min);
|
||||
w->setMaximum(max);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
static QWidget *createDoubleSpinBox(const RwMode &readOnly, QWidget *parent, double min, double max, int prec)
|
||||
{
|
||||
RWDoubleSpinBox *w = new RWDoubleSpinBox(readOnly, parent);
|
||||
w->setMinimum(min);
|
||||
w->setMaximum(max);
|
||||
w->setDecimals(prec);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
QWidget *DetailView::createWidget(const RwMode &readOnly,
|
||||
const dds::core::xtypes::DynamicType &type,
|
||||
const QString &name,
|
||||
NGL::INGLLogger &logger)
|
||||
{
|
||||
QWidget *parent = nullptr; // TODO: remove parent var
|
||||
QWidget *g = nullptr;
|
||||
|
||||
bool noNaming = false;
|
||||
switch (type.kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
g = new RWBitfieldWidget(readOnly,parent);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
g = createSpinBox(readOnly, parent, std::numeric_limits<short>::lowest(), std::numeric_limits<short>::max());
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
g = createSpinBox(readOnly, parent, std::numeric_limits<unsigned short>::lowest(), std::numeric_limits<unsigned short>::max());
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
g = createSpinBox(readOnly, parent, std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
g = createDoubleSpinBox(readOnly, parent, std::numeric_limits<quint32>::lowest(), std::numeric_limits<quint32>::max(), 0);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
g = createDoubleSpinBox(readOnly, parent, std::numeric_limits<quint64>::lowest(), std::numeric_limits<quint64>::max(), 0);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
g = createDoubleSpinBox(readOnly, parent, std::numeric_limits<qint64>::lowest(), std::numeric_limits<qint64>::max(), 0);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
g = createDoubleSpinBox(readOnly, parent, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max(), 6);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
g = createDoubleSpinBox(readOnly, parent, std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 13);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
g = new RWCheckBox(readOnly, parent);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::EnumType &enumType = static_cast<const dds::core::xtypes::EnumType &>(type);
|
||||
RWComboBox *w = new RWComboBox(readOnly, parent);
|
||||
for (const dds::core::xtypes::EnumMember &enumMember : enumType.members())
|
||||
w->addItem(QString::fromStdString(enumMember.name()), enumMember.ordinal());
|
||||
g = w;
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
g = createSpinBox(readOnly, parent, std::numeric_limits<char>::lowest(), std::numeric_limits<char>::max());
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::StringType &stringType = static_cast<const dds::core::xtypes::StringType &>(type);
|
||||
RWLineEdit *w = new RWLineEdit(readOnly, parent);
|
||||
w->setMaxLength(stringType.bounds());
|
||||
g = w;
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE:
|
||||
{
|
||||
//noNaming = true;
|
||||
g = name.contains(QRegExp(QLatin1String(".([0-9]*)$"))) ? new QWidget(parent) : new QGroupBox(name, parent);
|
||||
|
||||
QGridLayout *gl = new QGridLayout(g);
|
||||
|
||||
if (type.name() == "Core::GeoPosition" ||
|
||||
type.name() == "Core::GeoPosition2D")
|
||||
{
|
||||
QPushButton *mgrsBtn = new QPushButton(tr("MGRS"), g);
|
||||
mgrsBtn->setObjectName(name);
|
||||
mgrsBtn->setProperty("memberName", name);
|
||||
QLabel *mgrsLabel = new QLabel(g);
|
||||
mgrsLabel->setObjectName(name);
|
||||
mgrsLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
|
||||
CoordDetailDlg *coordDlg = new CoordDetailDlg(mgrsBtn);
|
||||
|
||||
auto cbAccepted = [g, coordDlg, mgrsBtn]()
|
||||
{
|
||||
const QPointF p = coordDlg->coordinate();
|
||||
const QString name = mgrsBtn->objectName();
|
||||
RWDoubleSpinBox *sbLon = g->findChild<RWDoubleSpinBox *>(name + QLatin1String(".lon"));
|
||||
RWDoubleSpinBox *sbLat = g->findChild<RWDoubleSpinBox *>(name + QLatin1String(".lat"));
|
||||
if (sbLon)
|
||||
sbLon->setValue(p.x());
|
||||
if (sbLat)
|
||||
sbLat->setValue(p.y());
|
||||
};
|
||||
connect(coordDlg, &CoordDetailDlg::accepted, mgrsBtn, cbAccepted);
|
||||
|
||||
auto cbClicked = [g, mgrsBtn, coordDlg]()
|
||||
{
|
||||
const QString name = mgrsBtn->objectName();
|
||||
RWDoubleSpinBox *sbLon = g->findChild<RWDoubleSpinBox *>(name + QLatin1String(".lon"));
|
||||
RWDoubleSpinBox *sbLat = g->findChild<RWDoubleSpinBox *>(name + QLatin1String(".lat"));
|
||||
if (sbLon && sbLat)
|
||||
coordDlg->setValues(sbLon->value(), sbLat->value());
|
||||
coordDlg->exec();
|
||||
};
|
||||
connect(mgrsBtn, &QPushButton::clicked, g, cbClicked);
|
||||
|
||||
gl->addWidget(mgrsBtn, 0, 0, 1, -1, Qt::AlignLeft);
|
||||
gl->addWidget(mgrsLabel, 0, 1, 1, -1);
|
||||
generateUi(readOnly, type, g, name, logger, gl);
|
||||
RWDoubleSpinBox *sbLon = g->findChild<RWDoubleSpinBox *>(name + QLatin1String(".lon"));
|
||||
RWDoubleSpinBox *sbLat = g->findChild<RWDoubleSpinBox *>(name + QLatin1String(".lat"));
|
||||
if (sbLon && sbLat)
|
||||
{
|
||||
auto cbUpdateLabel = [sbLon, sbLat, mgrsLabel]()
|
||||
{
|
||||
mgrsLabel->setText(LtsCoordTrans::mgrsFromGeographic(sbLon->value(), sbLat->value(), 7));
|
||||
};
|
||||
connect(sbLon, &RWDoubleSpinBox::valueChanged, mgrsLabel, cbUpdateLabel);
|
||||
connect(sbLat, &RWDoubleSpinBox::valueChanged, mgrsLabel, cbUpdateLabel);
|
||||
}
|
||||
}
|
||||
else
|
||||
generateUi(readOnly, type, g, name, logger, gl);
|
||||
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::SequenceType &seqType = static_cast<const dds::core::xtypes::SequenceType &>(type);
|
||||
|
||||
AbstractSequenceWidget *seq;
|
||||
if(dds::core::xtypes::is_primitive_type(seqType.content_type()))
|
||||
seq = new ListSequenceWidget(readOnly, name, seqType.content_type(), logger, parent);
|
||||
else
|
||||
seq = new TabSequenceWidget(readOnly, name, seqType.content_type(), logger, parent);
|
||||
|
||||
g = seq;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g && !noNaming)
|
||||
g->setObjectName(name);
|
||||
|
||||
DEBUG_LOG(logger) << "createWidget: new widget name: " << name;
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
|
||||
QWidget *DetailView::getWidget(const dds::core::xtypes::DynamicType &type, const QString &name)
|
||||
{
|
||||
QWidget *g = createWidget(m_readonly, type, name, m_logger);
|
||||
|
||||
if (!g)
|
||||
{
|
||||
QMap<dds::core::xtypes::TypeKind, QString> unmappedKinds; // when one of these types gets implemented, it should be removed from this map
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::NO_TYPE, QLatin1String("NO_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::PRIMITIVE_TYPE, QLatin1String("PRIMITIVE_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::CONSTRUCTED_TYPE, QLatin1String("CONSTRUCTED_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::COLLECTION_TYPE, QLatin1String("COLLECTION_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::AGGREGATION_TYPE, QLatin1String("AGGREGATION_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::ANNOTATION_TYPE, QLatin1String("ANNOTATION_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::UINT_8_TYPE, QLatin1String("UINT_8_TYPE"));
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::CHAR_32_TYPE, QLatin1String("CHAR_32_TYPE"));
|
||||
#else
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::CHAR_16_TYPE, QLatin1String("CHAR_16_TYPE"));
|
||||
#endif
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::BITSET_TYPE, QLatin1String("BITSET_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::ALIAS_TYPE, QLatin1String("ALIAS_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::ARRAY_TYPE, QLatin1String("ARRAY_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::SEQUENCE_TYPE, QLatin1String("SEQUENCE_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::WSTRING_TYPE, QLatin1String("WSTRING_TYPE"));
|
||||
// misusing QT_VERSION_CHECK macro :)
|
||||
#if QT_VERSION_CHECK(RTI_DDS_VERSION_MAJOR, RTI_DDS_VERSION_MINOR, RTI_DDS_VERSION_RELEASE) <= QT_VERSION_CHECK(6, 0, 0)
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::MAP_TYPE, QLatin1String("MAP_TYPE"));
|
||||
#else
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::MAP_TYPE_OMG, QLatin1String("MAP_TYPE"));
|
||||
#endif
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::UNION_TYPE, QLatin1String("UNION_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::UNION_FWD_DECL_TYPE, QLatin1String("UNION_FWD_DECL_TYPE"));
|
||||
unmappedKinds.insert(dds::core::xtypes::TypeKind::STRUCTURE_FWD_DECL_TYPE, QLatin1String("STRUCTURE_FWD_DECL_TYPE"));
|
||||
|
||||
QString kind;
|
||||
if (unmappedKinds.contains(type.kind().underlying()))
|
||||
kind = unmappedKinds.value(type.kind().underlying());
|
||||
else
|
||||
kind = QLatin1String("unknown kind");
|
||||
|
||||
//FATAL_LOG(m_logger) << "TypeKind not mapped: " << kind;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RWWidget *w = qobject_cast<RWWidget *>(g);
|
||||
if (w)
|
||||
connect(w, &RWWidget::writableRequested, this, &DetailView::onWritableRequested);
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
void DetailView::onWritableRequested()
|
||||
{
|
||||
const auto children = findChildren<QWidget *>();
|
||||
for (QWidget *child : children)
|
||||
{
|
||||
RWWidget *w = qobject_cast<RWWidget *>(child);
|
||||
if (w)
|
||||
w->onSetWritable();
|
||||
}
|
||||
}
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Widget to dynamically show dds::core::xtypes::DynamicData.
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <dds/dds.hpp>
|
||||
#include "rw/rwwidgets.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
|
||||
class DetailView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DetailView(QWidget *parent = nullptr);
|
||||
~DetailView();
|
||||
|
||||
static QWidget *createWidget(const RwMode &readOnly,
|
||||
const dds::core::xtypes::DynamicType &type,
|
||||
const QString &name,
|
||||
NGL::INGLLogger &logger);
|
||||
static bool generateUi(const RwMode &readOnly,
|
||||
const dds::core::xtypes::DynamicType &type,
|
||||
QWidget *parentWidget,
|
||||
const QString &name,
|
||||
NGL::INGLLogger &logger,
|
||||
QGridLayout *grid = nullptr);
|
||||
|
||||
const QString getTopicName() const { return m_topicName; }
|
||||
static bool setWidgetData(const dds::core::xtypes::DynamicData &data,
|
||||
const dds::core::xtypes::TypeKind &typeKind,
|
||||
uint32_t index,
|
||||
const QString &name,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger);
|
||||
static void getWidgetData(dds::core::xtypes::DynamicData &data,
|
||||
const dds::core::xtypes::TypeKind &typeKind,
|
||||
uint32_t index,
|
||||
const QString &name,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger);
|
||||
|
||||
QSharedPointer<dds::core::xtypes::DynamicData> getData() const;
|
||||
public slots:
|
||||
bool init(const dds::core::xtypes::DynamicData &data, const QString &topicName, RwMode rwMode = RwMode::ReadOnly);
|
||||
bool init(const dds::core::xtypes::DynamicType &type, const QString &topicName, RwMode rwMode = RwMode::ReadOnly);
|
||||
bool setData(const dds::core::xtypes::DynamicData &data) {return setData(data, QString());}
|
||||
void clear();
|
||||
|
||||
private slots:
|
||||
void onWritableRequested();
|
||||
|
||||
private:
|
||||
template <typename T, typename S>
|
||||
static void getSpinBoxValue(const QString &name,
|
||||
dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger);
|
||||
|
||||
|
||||
|
||||
template <typename T, typename S>
|
||||
static void setSpinBoxValue(S *b, const T &value);
|
||||
|
||||
static void getComboBoxValue(const QString &name,
|
||||
dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger);
|
||||
|
||||
static void setComboBoxValue(RWComboBox *w, int ordinal);
|
||||
|
||||
template <typename T, typename B>
|
||||
static void getBitfieldValue(const QString &name,
|
||||
dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QWidget *useOn,
|
||||
NGL::INGLLogger &logger);
|
||||
|
||||
template <typename T, typename B>
|
||||
static void setBitfieldValue(B *b, const T &value);
|
||||
|
||||
QWidget *getWidget(const dds::core::xtypes::DynamicType &type, const QString &name);
|
||||
bool setData(const dds::core::xtypes::DynamicData &data, const QString &name);
|
||||
|
||||
dds::core::xtypes::DynamicType m_type;
|
||||
RwMode m_readonly;
|
||||
QString m_topicName;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Abstract Dialog with ButtonBox and some pass-throughs
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "detailviewdlg.h"
|
||||
#include "ui_detailviewdlg.h"
|
||||
|
||||
#include "detailview.h"
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
DetailViewDlg::DetailViewDlg(QWidget *parent)
|
||||
: NGLDialog(parent)
|
||||
, m_ui(new Ui::DetailViewDlg)
|
||||
, m_bConnected(false)
|
||||
, m_logger(DdsView::getLogger("DetailView"))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onClose, this, &DetailViewDlg::accept);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onSend, this, &DetailViewDlg::onSend);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onOk, this, &DetailViewDlg::onOk);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onCancel, this, &DetailViewDlg::reject);
|
||||
}
|
||||
|
||||
DetailViewDlg::~DetailViewDlg()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void DetailViewDlg::setBtnsOkCancel()
|
||||
{
|
||||
m_ui->nglDialogButtonBox->setStandardButtons(NGLStandardButton::Ok | NGLStandardButton::Cancel);
|
||||
}
|
||||
|
||||
void DetailViewDlg::setBtnsSendOkCancel()
|
||||
{
|
||||
m_ui->nglDialogButtonBox->setStandardButtons(NGLStandardButton::Send | NGLStandardButton::Ok | NGLStandardButton::Cancel);
|
||||
setConnected(m_bConnected);
|
||||
}
|
||||
|
||||
void DetailViewDlg::setBtnsSendClose()
|
||||
{
|
||||
m_ui->nglDialogButtonBox->setStandardButtons(NGLStandardButton::Close | NGLStandardButton::Send);
|
||||
setConnected(m_bConnected);
|
||||
}
|
||||
|
||||
void DetailViewDlg::setConnected(bool bConnected)
|
||||
{
|
||||
m_bConnected = bConnected;
|
||||
m_ui->nglDialogButtonBox->setEnabled(NGLStandardButton::Send, m_bConnected);
|
||||
}
|
||||
|
||||
|
||||
bool DetailViewDlg::init(const dds::core::xtypes::DynamicData &data, const QString &topicName, RwMode mode)
|
||||
{
|
||||
bool retVal = m_ui->detailView->init(data, topicName, mode);
|
||||
setWindowTitle(m_ui->detailView->windowTitle());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool DetailViewDlg::init(const dds::core::xtypes::DynamicType &type, const QString &topicName, RwMode mode)
|
||||
{
|
||||
bool retVal = m_ui->detailView->init(type, topicName, mode);
|
||||
setWindowTitle(m_ui->detailView->windowTitle());
|
||||
QTimer::singleShot(0, this, [this]() { resize(sizeHint());});
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool DetailViewDlg::fill(const dds::core::xtypes::DynamicData &data)
|
||||
{
|
||||
bool retVal = m_ui->detailView->setData(data);
|
||||
m_ui->scrollArea->resize(m_ui->detailView->size());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void DetailViewDlg::onSend()
|
||||
{
|
||||
const QString topicName = m_ui->detailView->getTopicName();
|
||||
auto val = m_ui->detailView->getData();
|
||||
if (val)
|
||||
emit send(val, topicName);
|
||||
}
|
||||
|
||||
void DetailViewDlg::onOk()
|
||||
{
|
||||
auto val = m_ui->detailView->getData();
|
||||
if (val)
|
||||
{
|
||||
const QString topicName = m_ui->detailView->getTopicName();
|
||||
emit dataChanged(val, topicName);
|
||||
accept();
|
||||
}
|
||||
else
|
||||
reject();
|
||||
}
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Abstract Dialog with ButtonBox and some pass-throughs
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nglwidgets/ngldialog.h>
|
||||
#include <dds/dds.hpp>
|
||||
#include "detailview.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class DetailViewDlg;
|
||||
}
|
||||
|
||||
class DetailViewDlg : public NGLDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DetailViewDlg(QWidget *parent = nullptr);
|
||||
~DetailViewDlg();
|
||||
|
||||
bool init(const dds::core::xtypes::DynamicData &data, const QString &topicName, RwMode = RwMode::ReadWrite);
|
||||
bool init(const dds::core::xtypes::DynamicType &type, const QString &topicName, RwMode = RwMode::ReadWrite);
|
||||
bool fill(const dds::core::xtypes::DynamicData &data);
|
||||
|
||||
void setBtnsOkCancel();
|
||||
void setBtnsSendOkCancel();
|
||||
void setBtnsSendClose();
|
||||
void setConnected(bool bConnected);
|
||||
private slots:
|
||||
void onSend();
|
||||
void onOk();
|
||||
|
||||
signals:
|
||||
void send(const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName);
|
||||
void dataChanged(const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName);
|
||||
|
||||
private:
|
||||
Ui::DetailViewDlg *m_ui;
|
||||
bool m_bConnected;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Initialisation for Subscriber
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "initialisation.h"
|
||||
#include "subscriber.h"
|
||||
|
||||
#include <DDS/DDSConnector/publisher.h>
|
||||
|
||||
namespace DdsView
|
||||
{
|
||||
|
||||
Initialisation::Initialisation(const dds::domain::DomainParticipant &dp,
|
||||
int domainId, IDL::PartitionType partitionType,
|
||||
int exerciseId, TelegramModel *tm)
|
||||
: DDSConnector::DdsConnector(domainId, partitionType, exerciseId)
|
||||
, m_logger(DdsView::getLogger("Initialisation"))
|
||||
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Initialisation for partition: " << getDefaultPartition();
|
||||
initDDS(dp, tm);
|
||||
}
|
||||
|
||||
void Initialisation::initDDS(const dds::domain::DomainParticipant &dp, TelegramModel *tm)
|
||||
{
|
||||
DDSConnector::DdsConnector::init();
|
||||
|
||||
// Initialisation Subscriber
|
||||
setSubscriber(new DdsView::Subscriber(dp, getDefaultPartition(), tm));
|
||||
|
||||
// Initialisation Publisher
|
||||
// Note (!): The publisher has its own domainParticipant
|
||||
setPublisher(new DDSConnector::Publisher(dds::domain::DomainParticipant(getDomainId()), getDefaultPartition()));
|
||||
|
||||
if (getPublisher()->getDomainParticipant() == nullptr)
|
||||
return;
|
||||
|
||||
// Enable DomainParticipant
|
||||
// Default-QoS Configuration for enabled => false (using Qos from DefaultParticipant)
|
||||
dds::domain::DomainParticipant &pubDp = const_cast<dds::domain::DomainParticipant &>(getPublisher()->getDomainParticipant());
|
||||
pubDp->enable();
|
||||
}
|
||||
|
||||
bool Initialisation::initDDTCs(const QVector<DDSGeneric::TopicDescription> &vDro)
|
||||
{
|
||||
DdsView::Subscriber *sub = dynamic_cast<DdsView::Subscriber *>(getSubscriber());
|
||||
return sub ? sub->initDDTCs(vDro) : false;
|
||||
}
|
||||
|
||||
bool Initialisation::publish(const dds::core::xtypes::DynamicData &data, const std::string &topicName)
|
||||
{
|
||||
return getPublisher() ? getPublisher()->write(data, topicName) : false;
|
||||
}
|
||||
|
||||
} // Namespace DdsView
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Initialisation for Subscriber
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
#include <DDS/Generic/topic_description.h>
|
||||
#include "logger.h"
|
||||
|
||||
namespace DDSConnector
|
||||
{
|
||||
class Publisher;
|
||||
}
|
||||
|
||||
class TelegramModel;
|
||||
namespace DdsView
|
||||
{
|
||||
|
||||
class Initialisation : public DDSConnector::DdsConnector
|
||||
{
|
||||
public:
|
||||
Initialisation(const dds::domain::DomainParticipant &dp,
|
||||
int domainId, IDL::PartitionType partitionType,
|
||||
int exerciseId, TelegramModel *tm);
|
||||
~Initialisation() = default;
|
||||
|
||||
bool initDDTCs(const QVector<DDSGeneric::TopicDescription> &vDro);
|
||||
|
||||
// PublishData
|
||||
bool publish(const dds::core::xtypes::DynamicData &data, const std::string &topicName);
|
||||
|
||||
private:
|
||||
void initDDS(const dds::domain::DomainParticipant &dp, TelegramModel *tm);
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
log4j.reset=true
|
||||
log4j.rootLogger=WARN, file
|
||||
|
||||
# DdsView (DataSampleGenerator/DataSampleReceiver)
|
||||
###########################################################
|
||||
|
||||
log4j.logger.TopicDiscoverer=ERROR
|
||||
log4j.logger.TopicDiscoverer.BuiltinSubscriptionListener=ERROR
|
||||
log4j.logger.TopicDiscoverer.BuiltinPublicationListener=ERROR
|
||||
|
||||
log4j.logger.DataSampleReceiver.Initialisation=WARN
|
||||
log4j.logger.DataSampleReceiver.Subscriber=WARN
|
||||
|
||||
log4j.logger.DataSampleGenerator.main=WARN
|
||||
log4j.logger.DataSampleGenerator.Initialisation=WARN
|
||||
log4j.logger.DataSampleGenerator.PlayerData=WARN
|
||||
log4j.logger.DataSampleGenerator.Settings=WARN
|
||||
log4j.logger.DataSampleGenerator.Publisher=INFO
|
||||
#log4j.logger.DataSampleGenerator.Publisher=DEBUG
|
||||
log4j.logger.GenericBase=WARN
|
||||
log4j.logger.Generic.DynamicDataWriter=INFO
|
||||
log4j.logger.Generic.DynamicDataReader=INFO
|
||||
log4j.logger.Generic.DataWriter=INFO
|
||||
log4j.logger.Generic.DataReader=INFO
|
||||
log4j.logger.Generic.Subscriber=INFO
|
||||
log4j.logger.Generic.DTC=INFO
|
||||
log4j.logger.Generic.DTS=INFO
|
||||
log4j.logger.DTC=INFO
|
||||
log4j.logger.DTC.SubscriberListener=WARN
|
||||
log4j.logger.DTS=INFO
|
||||
log4j.logger.DDSConnector=INFO
|
||||
log4j.logger.DdsView=DEBUG
|
||||
log4j.logger.DdsView.Subscriber=INFO
|
||||
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.MaxFileSize=1MB
|
||||
log4j.appender.file.MaxBackupIndex=9
|
||||
log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_ddsview.log
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
|
||||
|
||||
|
||||
# Appnd_Console
|
||||
###########################################################
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
|
||||
|
||||
|
||||
# Appnd_Syslog
|
||||
###########################################################
|
||||
log4j.appender.syslog=org.apache.log4j.SyslogAppender
|
||||
log4j.appender.syslog.Threshold=ERROR
|
||||
log4j.appender.syslog.SysLogHost=localhost
|
||||
log4j.appender.syslog.Facility=LOCAL7
|
||||
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
|
||||
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
|
||||
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
|
||||
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
###########################################################
|
||||
# 1) Available Levels in LOG4CXX Version 0.10.0:
|
||||
# TRACE,
|
||||
# DEBUG,
|
||||
# INFO,
|
||||
# WARN,
|
||||
# ERROR
|
||||
# FATAL
|
||||
#
|
||||
# d = date/time
|
||||
# p = priority (Level)
|
||||
# c = category/channel
|
||||
# m = message
|
||||
# F = file
|
||||
# L = line
|
||||
# n = platform dependent line separator character or characters.
|
||||
# ....
|
||||
#
|
||||
# Examples for console:
|
||||
# a)
|
||||
# %d %p %c #%m# (%.50F:%L)%n
|
||||
# date/time
|
||||
# priority
|
||||
# category/channel
|
||||
# #message#
|
||||
# (Maximal 50Character des Filename von rechts:Line)
|
||||
# line separator
|
||||
#
|
||||
# b)
|
||||
# %d (%.15F:%L) %-5p %c |#| %m%n
|
||||
# date/time
|
||||
# (Maximal 15Character des Filename von rechts:Line)
|
||||
# priority, Right pad with spaces if the priority name is less than 5 characters long.
|
||||
# category/channel
|
||||
# message
|
||||
# line separator
|
||||
#
|
||||
# Examples for files:
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Building Config - logging facility
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace DdsView
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "DdsView.";
|
||||
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 DdsView
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Logging for ParticipantManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
namespace DdsView
|
||||
{
|
||||
typedef NGL::INGLLogger &LOGGER_T;
|
||||
|
||||
LOGGER_T getLogger(const char *name);
|
||||
inline LOGGER_T getLogger(const QString &loggerName)
|
||||
{
|
||||
return getLogger(loggerName.toUtf8().constData());
|
||||
}
|
||||
} // namespace DdsView
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* DDS View -> The "LogView" for DDS
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "nglsettings.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <LtsCommandLineParser/LtsCommandLineParser.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <nglapplication/nglapplication.h>
|
||||
#include <LtsPluginManager/lts_plugin_loader.h>
|
||||
#include <DDS/QosProfile/DDS_DefaultQos.h>
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QUrl>
|
||||
|
||||
|
||||
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
|
||||
{
|
||||
public:
|
||||
CmdLineParser() : LtsCommandLineParser::LtsCommandLineParser(ProvideCommandLineParameterOptions(LtsCommandLineParser::CreateDomainIdFromIp |
|
||||
LtsCommandLineParser::DomainId |
|
||||
LtsCommandLineParser::ExerciseId |
|
||||
LtsCommandLineParser::TcpConnectionUrl))
|
||||
{}
|
||||
const char *langCode() const { return m_langCode.data(); }
|
||||
|
||||
bool init()
|
||||
{
|
||||
setDomainId(-1);
|
||||
QCommandLineOption langCodeOption(QStringList() << QLatin1String("l") << QLatin1String("langCode"),
|
||||
QLatin1String("Language."),
|
||||
QLatin1String("en_US|de_DE"),
|
||||
QLatin1String("en_US"));
|
||||
addOption(langCodeOption);
|
||||
|
||||
if (!LtsCommandLineParser::init())
|
||||
return false;
|
||||
|
||||
if (isSet(langCodeOption))
|
||||
m_langCode = value(langCodeOption).toLatin1();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray m_langCode;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NGLApplication app(argc, argv);
|
||||
app.createLogger("DdsView", "log4cxx_ddsview.props");
|
||||
NGL::INGLLogger &logger = app.baseLogger();
|
||||
QCoreApplication::setApplicationName(QLatin1String("DDS View"));
|
||||
QCoreApplication::setOrganizationName(QLatin1String("RME"));
|
||||
|
||||
// load style plugin to get access to icons
|
||||
LtsPluginManager::LtsPluginLoader::getInstance()->loadPlugins(LtsPluginManager::PluginType::STYLE, nullptr, {QStringLiteral("rme.light")});
|
||||
|
||||
CmdLineParser options;
|
||||
if (!options.init())
|
||||
return -1;
|
||||
|
||||
// NGL::Settings settings(app.organizationName(), app.applicationName());
|
||||
|
||||
STARTUP_LOG(logger);
|
||||
|
||||
// NGL::init_translator(options.langCode());
|
||||
|
||||
int exerciseId = options.exerciseId();
|
||||
// TODO: max-Domain-Id? Prüfen...
|
||||
int domainId = options.domainId();
|
||||
if (domainId < 0)
|
||||
{
|
||||
bool bOk = false;
|
||||
domainId = QInputDialog::getInt(nullptr,
|
||||
QObject::tr("Select DDS Domain"),
|
||||
QObject::tr("Domain Id"),
|
||||
NGLSettings::instance()->ddsDomain(), 0, 999, 1, &bOk);
|
||||
if (!bOk)
|
||||
{
|
||||
printf("Startup canceled by user\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
NGLSettings::instance()->setDdsDomain(domainId);
|
||||
MainWindow *mw = new MainWindow(domainId, exerciseId, options.useTcpConnection() ?
|
||||
DDS::QosProfile::ConnectionType::Tcp :
|
||||
DDS::QosProfile::ConnectionType::Client,
|
||||
options.tcpConnectionUrl());
|
||||
|
||||
mw->show();
|
||||
|
||||
app.setQuitOnLastWindowClosed(true);
|
||||
|
||||
int retval = app.exec();
|
||||
|
||||
SHUTDOWN_LOG(logger);
|
||||
|
||||
return retval;
|
||||
}
|
||||
Executable
+468
@@ -0,0 +1,468 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* MainWindow for DDS View
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include "nglsettings.h"
|
||||
#include "zwmsui_version.h"
|
||||
#include "telegramview.h"
|
||||
#include "detailviewdlg.h"
|
||||
#include "topic_tree_model.h"
|
||||
#include "topicselector.h"
|
||||
#include "sampleview.h"
|
||||
|
||||
#include <DDS/Generic/topic_type_plugin.h>
|
||||
|
||||
#include <TopicDiscover/topic_discoverer.h>
|
||||
#include <LtsPluginManager/lts_plugin_loader.h>
|
||||
|
||||
#include <QMenuBar>
|
||||
|
||||
MainWindow::MainWindow(int domainId, int exerciseId, DDS::QosProfile::ConnectionType ct, const QUrl &tcpDiscoveryUrl, QWidget *parent)
|
||||
: NGLMainWindow(parent)
|
||||
, m_dsr(nullptr)
|
||||
, m_ddsCredentials()
|
||||
, m_topicDiscoverer(new TopicDiscoverer(domainId, this))
|
||||
, m_dataSortModel(new TopicSortModel(this))
|
||||
, m_functionSortModel(new TopicSortModel(this))
|
||||
, m_ui(new Ui::MainWindow)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_ui->sbExercise->setValue(exerciseId);
|
||||
|
||||
setWindowTitle(tr("DDS View - Domain %1").arg(domainId));
|
||||
m_ui->splitter->setSizes(QList<int>() << 550 << 750);
|
||||
|
||||
m_menu[QLatin1String("Edit")] = new QMenu(tr("Edit"));
|
||||
m_menuBar->insertMenu(m_menu[QLatin1String("Help")]->menuAction(), m_menu[QLatin1String("Edit")]);
|
||||
|
||||
m_menu[QLatin1String("View")] = new QMenu(tr("View"));
|
||||
m_menuBar->insertMenu(m_menu[QLatin1String("Help")]->menuAction(), m_menu[QLatin1String("View")]);
|
||||
|
||||
QAction *action;
|
||||
|
||||
action = m_ui->dwDetailViewTree->toggleViewAction();
|
||||
action->setText(tr("Toggle Details (tree)"));
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+t")));
|
||||
m_menu[QLatin1String("View")]->addAction(action);
|
||||
|
||||
action = new QAction(tr("Load Sample List"), this);
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+o")));
|
||||
connect(action, &QAction::triggered, m_ui->sampleView, &SampleView::onLoadSampleList);
|
||||
m_menu[QLatin1String("File")]->insertAction(m_fileActionBefore, action);
|
||||
|
||||
action = new QAction(tr("Save Sample List"), this);
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+s")));
|
||||
connect(action, &QAction::triggered, m_ui->sampleView, &SampleView::onSaveSampleList);
|
||||
m_menu[QLatin1String("File")]->insertAction(m_fileActionBefore, action);
|
||||
|
||||
action = new QAction(tr("Configure Details"), this);
|
||||
connect(action, &QAction::triggered, this, &MainWindow::onConfigureDetails);
|
||||
m_menu[QLatin1String("Edit")]->addAction(action);
|
||||
|
||||
setVersionView();
|
||||
|
||||
m_treeSrcModelData = new TreeModel(m_ui->trvDataTopics);
|
||||
m_dataSortModel->setSourceModel(m_treeSrcModelData);
|
||||
m_ui->trvDataTopics->setModel(m_dataSortModel);
|
||||
m_dataSortModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
m_treeSrcModelFunction = new TreeModel(m_ui->trvFunctionTopics);
|
||||
m_functionSortModel->setSourceModel(m_treeSrcModelFunction);
|
||||
m_ui->trvFunctionTopics->setModel(m_functionSortModel);
|
||||
m_functionSortModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
m_topicDataModel = new TopicDataModel(m_ui->trvTopicData);
|
||||
m_ui->trvTopicData->setModel(m_topicDataModel);
|
||||
m_ui->trvTopicData->setColumnHidden(2, true);
|
||||
m_ui->trvTopicData->setColumnHidden(3, true);
|
||||
m_ui->trvTopicData->setColumnHidden(4, true);
|
||||
|
||||
connect(m_ui->trvDataTopics->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &MainWindow::onReaderChanged);
|
||||
connect(m_ui->trvFunctionTopics->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &MainWindow::onSelectionFunctionChanged);
|
||||
|
||||
connect(m_ui->sbExercise, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::onReaderChanged);
|
||||
|
||||
connect(m_ui->pbConnect, &QPushButton::clicked, this, &MainWindow::onConnect);
|
||||
connect(m_ui->pbDisconnect, &QPushButton::clicked, this, &MainWindow::onDisconnect);
|
||||
|
||||
connect(m_ui->pbPlay, &QPushButton::clicked, m_ui->sampleView, &SampleView::onPlayChecked);
|
||||
connect(m_ui->cbRepeat, &QCheckBox::clicked, m_ui->sampleView, &SampleView::onSetRepeat);
|
||||
connect(m_ui->sampleView, &SampleView::playerActive, m_ui->pbPlay, &QToolButton::setChecked);
|
||||
|
||||
m_ui->pbDisconnect->setEnabled(false);
|
||||
m_ddsCredentials.domain = -1;
|
||||
|
||||
connect(m_topicDiscoverer, &TopicDiscoverer::topicDescription,
|
||||
this, &MainWindow::onTopicDescription);
|
||||
|
||||
m_topicDiscoverer->init(ct, tcpDiscoveryUrl);
|
||||
|
||||
connect(m_ui->telegramView, &TelegramView::dataClicked,
|
||||
m_topicDataModel, &TopicDataModel::setTopicData);
|
||||
connect(m_ui->telegramView, &TelegramView::clearDetailView,
|
||||
m_topicDataModel, &TopicDataModel::reset);
|
||||
connect(m_ui->telegramView, &TelegramView::send,
|
||||
this, &MainWindow::onSend);
|
||||
connect(m_ui->sampleView, &SampleView::send,
|
||||
this, &MainWindow::onSend);
|
||||
|
||||
loadPlugins();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
m_versionView->disconnect(this);
|
||||
delete m_ui;
|
||||
delete m_dsr;
|
||||
}
|
||||
|
||||
IDL::PartitionType MainWindow::partitionType() const
|
||||
{
|
||||
if (m_ui->rbExercise->isChecked())
|
||||
return IDL::PartitionType::Exercise;
|
||||
|
||||
if (m_ui->rbPlayback->isChecked())
|
||||
return IDL::PartitionType::Replay;
|
||||
|
||||
// Maybe we add all the other options later...
|
||||
// default:
|
||||
return IDL::PartitionType::Exercise;
|
||||
}
|
||||
|
||||
MainWindow::DdsCredentials MainWindow::onReaderChanged()
|
||||
{
|
||||
const QModelIndexList milDt = m_ui->trvDataTopics->selectionModel()->selectedRows();
|
||||
const QModelIndexList milFt = m_ui->trvFunctionTopics->selectionModel()->selectedRows();
|
||||
|
||||
QVector<DDSGeneric::TopicDescription> vTopicDescription;
|
||||
vTopicDescription.reserve(vTopicDescription.size() + milFt.size());
|
||||
|
||||
for (const QModelIndex &idx : milDt)
|
||||
{
|
||||
const DDSGeneric::TopicDescription topicDesc = m_dataSortModel->getData(idx);
|
||||
if (topicDesc.typeName.empty())
|
||||
continue;
|
||||
DEBUG_LOG(m_logger) << "selected data topic: " << topicDesc.name;
|
||||
vTopicDescription << topicDesc;
|
||||
}
|
||||
for (const QModelIndex &idx : milFt)
|
||||
{
|
||||
const DDSGeneric::TopicDescription topicDesc = m_functionSortModel->getData(idx);
|
||||
if (topicDesc.typeName.empty())
|
||||
continue;
|
||||
DEBUG_LOG(m_logger) << "selected function topic: " << topicDesc.name;
|
||||
vTopicDescription << topicDesc;
|
||||
}
|
||||
|
||||
DdsCredentials current(m_ui->sbExercise->value(), m_topicDiscoverer->getDomainParticipant().domain_id(), partitionType(), vTopicDescription);
|
||||
updateButtons(current);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
class ScopedBoolean
|
||||
{
|
||||
public:
|
||||
explicit ScopedBoolean(bool &b)
|
||||
: m_b(b)
|
||||
{
|
||||
m_b = true;
|
||||
}
|
||||
~ScopedBoolean()
|
||||
{
|
||||
m_b = false;
|
||||
}
|
||||
private:
|
||||
bool &m_b;
|
||||
};
|
||||
|
||||
void MainWindow::onSelectionFunctionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||
{
|
||||
static bool bInReaderChanged = false;
|
||||
if (bInReaderChanged)
|
||||
return;
|
||||
ScopedBoolean b(bInReaderChanged);
|
||||
|
||||
static QItemSelection itemSelection;
|
||||
QModelIndexList mil;
|
||||
if (!selected.isEmpty())
|
||||
mil = selected.indexes();
|
||||
else if (!deselected.isEmpty())
|
||||
mil = deselected.indexes();
|
||||
else
|
||||
return; //should never happen
|
||||
|
||||
if (mil.isEmpty())
|
||||
return;
|
||||
const QModelIndex index = mil.first();
|
||||
|
||||
if (index.column() != 0)
|
||||
return;
|
||||
|
||||
const DDSGeneric::TopicDescription topicDesc = m_functionSortModel->getData(index);
|
||||
if (topicDesc.typeName.empty()) // --> knot de(selected)
|
||||
{
|
||||
int childCount = m_functionSortModel->rowCount(index);
|
||||
if (childCount == 0)
|
||||
return;
|
||||
|
||||
const QModelIndex startindex = m_functionSortModel->index(0, index.column(), index);
|
||||
const QModelIndex endindex = m_functionSortModel->index(childCount - 1, index.column(), index);
|
||||
itemSelection.merge(QItemSelection(startindex, endindex), QItemSelectionModel::Select);
|
||||
}
|
||||
else
|
||||
{
|
||||
// one child (de)selected
|
||||
if (!selected.isEmpty())
|
||||
{
|
||||
bool allSelected = true;
|
||||
int childs = m_functionSortModel->rowCount(index.parent());
|
||||
|
||||
for (int count = 0; count < childs; count ++)
|
||||
{
|
||||
QModelIndex sibling = index.sibling(count, index.column());
|
||||
if (sibling == index)
|
||||
continue; // ignore same ModelIndex
|
||||
if (!m_ui->trvFunctionTopics->selectionModel()->isSelected(sibling))
|
||||
{
|
||||
allSelected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allSelected)
|
||||
itemSelection.merge(QItemSelection(index.parent(), index.parent()), QItemSelectionModel::Select);
|
||||
itemSelection.append(selected);
|
||||
}
|
||||
else if (!deselected.isEmpty())
|
||||
{
|
||||
itemSelection.merge(QItemSelection(index.parent(), index.parent()), QItemSelectionModel::Select);
|
||||
itemSelection.append(deselected);
|
||||
}
|
||||
else
|
||||
return; //should never happen
|
||||
}
|
||||
|
||||
if (!selected.isEmpty() && !itemSelection.isEmpty())
|
||||
{
|
||||
m_ui->trvFunctionTopics->selectionModel()->select(itemSelection, QItemSelectionModel::Select);
|
||||
m_ui->trvFunctionTopics->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
|
||||
}
|
||||
else if (!deselected.isEmpty())
|
||||
{
|
||||
m_ui->trvFunctionTopics->selectionModel()->select(itemSelection, QItemSelectionModel::Deselect);
|
||||
m_ui->trvFunctionTopics->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Deselect);
|
||||
}
|
||||
else
|
||||
return; //should never happen
|
||||
|
||||
itemSelection.clear();
|
||||
|
||||
ERROR_LOG(m_logger) << "cur: " << m_ui->trvFunctionTopics->selectionModel()->selectedIndexes().size();
|
||||
|
||||
onReaderChanged();
|
||||
}
|
||||
|
||||
void MainWindow::updateButtons(const MainWindow::DdsCredentials ¤t)
|
||||
{
|
||||
if (current == m_ddsCredentials || m_dsr == nullptr)
|
||||
{
|
||||
m_ui->pbConnect->setText(tr("Connect"));
|
||||
m_ui->pbConnect->setEnabled(m_dsr == nullptr);
|
||||
m_ui->pbDisconnect->setEnabled(m_dsr != nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->pbConnect->setText(tr("Switch"));
|
||||
m_ui->pbConnect->setEnabled(true);
|
||||
m_ui->pbDisconnect->setEnabled(true);
|
||||
}
|
||||
m_ui->telegramView->setConnected(m_dsr != nullptr);
|
||||
m_ui->sampleView->setConnected(m_dsr != nullptr);
|
||||
}
|
||||
|
||||
void MainWindow::onSend(const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName)
|
||||
{
|
||||
if (!m_dsr || !data)
|
||||
return;
|
||||
|
||||
dds::core::xtypes::DynamicData dataToSend(*data);
|
||||
if (dataToSend.member_exists("sampleTime"))
|
||||
{
|
||||
qint64 curValue = dataToSend.value<qint64>("sampleTime");
|
||||
if (curValue <= 1000000)
|
||||
dataToSend.value("sampleTime", QDateTime::currentMSecsSinceEpoch() + curValue);
|
||||
}
|
||||
m_dsr->publish(dataToSend, topicName.toStdString());
|
||||
}
|
||||
|
||||
void MainWindow::onConnect()
|
||||
{
|
||||
if (m_ui->trvDataTopics->selectionModel()->selectedRows().isEmpty() &&
|
||||
m_ui->trvFunctionTopics->selectionModel()->selectedRows().isEmpty())
|
||||
return;
|
||||
|
||||
// Todo: Change of exercise not regarded!
|
||||
DdsCredentials cred = onReaderChanged();
|
||||
|
||||
if (!m_dsr)
|
||||
m_dsr = new DdsView::Initialisation(m_topicDiscoverer->getDomainParticipant(),
|
||||
cred.domain,
|
||||
cred.partitionType,
|
||||
cred.exercise,
|
||||
m_ui->telegramView->telegramModel());
|
||||
|
||||
// Set DroTypes after Initialisation
|
||||
m_dsr->initDDTCs(cred.droTypes);
|
||||
|
||||
m_ddsCredentials = cred;
|
||||
updateButtons(cred);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::onDisconnect()
|
||||
{
|
||||
delete m_dsr;
|
||||
m_dsr = nullptr;
|
||||
onReaderChanged();
|
||||
}
|
||||
|
||||
void MainWindow::onConfigureDetails()
|
||||
{
|
||||
TopicSelector *ts = new TopicSelector(TopicSelector::Usage::TypeCodeInfos, this);
|
||||
connect(ts, &TopicSelector::typeCodeInfosChanged, m_ui->telegramView, &TelegramView::onTypeCodeInfosChanged);
|
||||
ts->show();
|
||||
}
|
||||
|
||||
void MainWindow::onTopicDescription(const std::string &topicName, const std::string &typeName, const dds::core::xtypes::StructType &type)
|
||||
{
|
||||
if (NGLSettings::instance()->hasDataTopic(topicName) ||
|
||||
NGLSettings::instance()->hasFunctionTopic(topicName))
|
||||
return;
|
||||
|
||||
TopicDescriptionEx topicDesc;
|
||||
topicDesc.name = topicName;
|
||||
topicDesc.typeName = typeName;
|
||||
topicDesc.type = type;
|
||||
topicDesc.serviceName = tr("DDS");
|
||||
|
||||
TreeItem *parent = nullptr;
|
||||
|
||||
DEBUG_LOG(m_logger) << "TypeName:" << typeName;
|
||||
|
||||
|
||||
if (QString::fromStdString(typeName).endsWith(QLatin1String("Request")) ||
|
||||
QString::fromStdString(typeName).endsWith(QLatin1String("Reply")))
|
||||
{
|
||||
// FunctionTopic
|
||||
m_functionSortModel->addData(topicDesc, parent);
|
||||
NGLSettings::instance()->insertFunctionTopic(topicDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// DataTopic
|
||||
m_dataSortModel->addData(topicDesc, parent);
|
||||
NGLSettings::instance()->insertDataTopic(topicDesc);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setVersionView()
|
||||
{
|
||||
m_versionView = new VersionView(NGLSettings::instance(), tr("DDS View"), this);
|
||||
connect(m_versionView, &VersionView::destroyed,
|
||||
this, &MainWindow::onVersionViewDeleted, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
bool MainWindow::loadPlugin(const LtsPluginManager::PluginInstance &pi)
|
||||
{
|
||||
DDSGeneric::TopicTypePlugin *typePlugin = qobject_cast<DDSGeneric::TopicTypePlugin *>(pi.plugin);
|
||||
if (!typePlugin)
|
||||
return false;
|
||||
|
||||
const QMap<std::string, DDSGeneric::TopicDescription> dataTopics = typePlugin->getDataTopics();
|
||||
for (const DDSGeneric::TopicDescription &td : dataTopics)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Loading data topic from service: " << typePlugin->getServiceShortName()
|
||||
<< ", topic: " << td.name;
|
||||
TopicDescriptionEx tdEx(td);
|
||||
tdEx.serviceName = typePlugin->getServiceShortName();
|
||||
NGLSettings::instance()->insertDataTopic(tdEx);
|
||||
|
||||
// nullptr -> no parent -> model must search correct parent on first level
|
||||
m_dataSortModel->addData(tdEx, nullptr);
|
||||
}
|
||||
|
||||
|
||||
struct RequestReplyStruct
|
||||
{
|
||||
DDSGeneric::TopicDescription ftRequest;
|
||||
DDSGeneric::TopicDescription ftReply;
|
||||
|
||||
bool hasRequestAndReply() const { return !ftRequest.isEmpty() && !ftReply.isEmpty(); }
|
||||
};
|
||||
|
||||
QMap<std::string, RequestReplyStruct> tempData;
|
||||
const QMap<std::string, DDSGeneric::TopicDescription> functionTopics = typePlugin->getFunctionTopics();
|
||||
for (const DDSGeneric::TopicDescription &td : functionTopics)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "req: " << typePlugin->getServiceShortName() << " - " << td.name;
|
||||
const QString topicName = QString::fromStdString(td.name);
|
||||
if (topicName.endsWith(QLatin1String("Reply")))
|
||||
{
|
||||
const QString key = topicName.left(topicName.size() - QLatin1String("Reply").size());
|
||||
tempData[key.toStdString()].ftReply = td;
|
||||
}
|
||||
else if (topicName.endsWith(QLatin1String("Request")))
|
||||
{
|
||||
const QString key = topicName.left(topicName.size() - QLatin1String("Request").size());
|
||||
tempData[key.toStdString()].ftRequest = td;
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Got unknown FTC/S topic: " << td.name;
|
||||
// should not happend at all - print warning
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = tempData.cbegin(); it != tempData.cend(); ++it)
|
||||
{
|
||||
const std::string &key = it.key();
|
||||
const RequestReplyStruct &rr = it.value();
|
||||
|
||||
TopicDescriptionEx tdKnot;
|
||||
tdKnot.serviceName = typePlugin->getServiceShortName();
|
||||
tdKnot.name = key;
|
||||
|
||||
// nullptr -> no parent -> model must search correct parent on first level
|
||||
TreeItem *knot = m_functionSortModel->addData(tdKnot, nullptr);
|
||||
|
||||
TopicDescriptionEx tdReply(rr.ftReply);
|
||||
tdReply.serviceName = typePlugin->getServiceShortName();
|
||||
m_functionSortModel->addData(tdReply, knot);
|
||||
NGLSettings::instance()->insertFunctionTopic(tdReply);
|
||||
|
||||
TopicDescriptionEx tdRequest(rr.ftRequest);
|
||||
tdRequest.serviceName = typePlugin->getServiceShortName();
|
||||
m_functionSortModel->addData(tdRequest, knot);
|
||||
NGLSettings::instance()->insertFunctionTopic(tdRequest);
|
||||
}
|
||||
|
||||
return true; // do not unload plugin because we have some static QString references in there!
|
||||
}
|
||||
|
||||
void MainWindow::loadPlugins()
|
||||
{
|
||||
LtsPluginManager::LtsPluginLoader::getInstance()->loadPlugins(LtsPluginManager::PluginType::TOPIC_TYPE,
|
||||
[&](const LtsPluginManager::PluginInstance & pi) { return loadPlugin(pi); });
|
||||
}
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* MainWindow for DDS View
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "initialisation.h"
|
||||
|
||||
#include <nglwidgets/nglmainwindow.h>
|
||||
#include <DDS/DDSConnector/subscriber.h>
|
||||
#include <QVariant>
|
||||
#include <QItemSelection>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
namespace DdsView
|
||||
{
|
||||
class Initialisation;
|
||||
}
|
||||
|
||||
namespace LtsPluginManager
|
||||
{
|
||||
struct PluginInstance;
|
||||
}
|
||||
|
||||
class TopicDataModel;
|
||||
class TopicSortModel;
|
||||
class TopicDiscoverer;
|
||||
struct TopicDescriptionEx;
|
||||
class TreeItem;
|
||||
class TreeModel;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// | Class MainWindow |
|
||||
// --------------------------------------------------------------------------------
|
||||
class MainWindow : public NGLMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(int domainId, int exerciseId,
|
||||
DDS::QosProfile::ConnectionType ct, const QUrl &tcpDiscoveryUrl,
|
||||
QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
private:
|
||||
TreeItem *getServiceKnot(TopicDescriptionEx &description, TreeModel *modelToSearch) const;
|
||||
struct DdsCredentials
|
||||
{
|
||||
int exercise = 0;
|
||||
int domain = 0;
|
||||
IDL::PartitionType partitionType = IDL::PartitionType::Exercise;
|
||||
QVector<DDSGeneric::TopicDescription> droTypes;
|
||||
|
||||
bool operator ==(const DdsCredentials &o) const
|
||||
{
|
||||
return (exercise == o.exercise && domain == o.domain && droTypes == o.droTypes && partitionType == o.partitionType);
|
||||
}
|
||||
DdsCredentials(int e = 0, int d = 0, IDL::PartitionType p = IDL::PartitionType::Exercise, const QVector<DDSGeneric::TopicDescription> &s = QVector<DDSGeneric::TopicDescription>())
|
||||
: exercise(e)
|
||||
, domain(d)
|
||||
, partitionType(p)
|
||||
, droTypes(s)
|
||||
{}
|
||||
};
|
||||
|
||||
DdsView::Initialisation *m_dsr;
|
||||
DdsCredentials m_ddsCredentials;
|
||||
TopicDiscoverer *m_topicDiscoverer;
|
||||
TopicSortModel *m_dataSortModel;
|
||||
TopicSortModel *m_functionSortModel;
|
||||
TopicDataModel *m_topicDataModel;
|
||||
Ui::MainWindow *m_ui;
|
||||
TreeModel *m_treeSrcModelData;
|
||||
TreeModel *m_treeSrcModelFunction;
|
||||
|
||||
void setVersionView();
|
||||
void loadPlugins();
|
||||
void updateButtons(const DdsCredentials ¤t);
|
||||
bool loadPlugin(const LtsPluginManager::PluginInstance &pi);
|
||||
|
||||
IDL::PartitionType partitionType() const;
|
||||
private slots:
|
||||
DdsCredentials onReaderChanged();
|
||||
void onSelectionFunctionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||
void onSend(const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName);
|
||||
void onConnect();
|
||||
void onDisconnect();
|
||||
void onConfigureDetails();
|
||||
void onTopicDescription(const std::string &topicName,
|
||||
const std::string &typeName,
|
||||
const dds::core::xtypes::StructType &type);
|
||||
};
|
||||
Executable
+136
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Settings for zwmsqt
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#include "nglsettings.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
static NGLSettings *s_instance = nullptr;
|
||||
static void cleanupSettings()
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
// class NGLSettings
|
||||
//
|
||||
NGLSettings::NGLSettings()
|
||||
: NGL::Settings(QLatin1String("gfs_ws.ini"))
|
||||
{
|
||||
s_instance = this;
|
||||
qAddPostRoutine(cleanupSettings);
|
||||
}
|
||||
|
||||
QMap<QString, QStringList> NGLSettings::infoValues()
|
||||
{
|
||||
QMap<QString, QStringList> map;
|
||||
settings()->beginGroup(QLatin1String("TelegramView"));
|
||||
int size = settings()->beginReadArray(QLatin1String("InfoValues"));
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
settings()->setArrayIndex(i);
|
||||
QString key = settings()->value(QLatin1String("TopicName")).toString();
|
||||
QStringList value = settings()->value(QLatin1String("Columns")).toString().split(QLatin1String("|"));
|
||||
map.insert(key, value);
|
||||
}
|
||||
settings()->endArray();
|
||||
settings()->endGroup();
|
||||
return map;
|
||||
}
|
||||
|
||||
void NGLSettings::setInfoValues(const QMap<QString, QStringList> &map)
|
||||
{
|
||||
settings()->beginGroup(QLatin1String("TelegramView"));
|
||||
settings()->beginWriteArray(QLatin1String("InfoValues"));
|
||||
auto mi = map.constBegin();
|
||||
int i = -1;
|
||||
for (; mi != map.constEnd(); ++mi)
|
||||
{
|
||||
settings()->setArrayIndex(++i);
|
||||
settings()->setValue(QLatin1String("TopicName"), mi.key());
|
||||
settings()->setValue(QLatin1String("Columns"), mi.value().join(QLatin1String("|")));
|
||||
}
|
||||
settings()->endArray();
|
||||
settings()->endGroup();
|
||||
}
|
||||
|
||||
int NGLSettings::ddsDomain() const
|
||||
{
|
||||
return settings()->value(QLatin1String("DomainId"), 0).toInt();
|
||||
}
|
||||
|
||||
void NGLSettings::setDdsDomain(int domainId)
|
||||
{
|
||||
settings()->setValue(QLatin1String("DomainId"), domainId);
|
||||
}
|
||||
|
||||
|
||||
NGLSettings *NGLSettings::instance()
|
||||
{
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = new NGLSettings;
|
||||
qAddPostRoutine(cleanupSettings);
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
QMap<std::string, TopicDescriptionEx> NGLSettings::functionTopics() const
|
||||
{
|
||||
return m_ftTypes;
|
||||
}
|
||||
|
||||
bool NGLSettings::hasFunctionTopic(const std::string &name) const
|
||||
{
|
||||
return m_ftTypes.contains(name);
|
||||
}
|
||||
|
||||
TopicDescriptionEx NGLSettings::functionTopic(const std::string &topicName) const
|
||||
{
|
||||
return m_ftTypes.value(topicName);
|
||||
}
|
||||
|
||||
void NGLSettings::insertFunctionTopic(const TopicDescriptionEx &td)
|
||||
{
|
||||
m_ftTypes.insert(td.name, td);
|
||||
}
|
||||
|
||||
QMap<std::string, TopicDescriptionEx> NGLSettings::dataTopics() const
|
||||
{
|
||||
return m_dtTypes;
|
||||
}
|
||||
|
||||
bool NGLSettings::hasDataTopic(const std::string &name) const
|
||||
{
|
||||
return m_dtTypes.contains(name);
|
||||
}
|
||||
|
||||
TopicDescriptionEx NGLSettings::dataTopic(const std::string &topicName) const
|
||||
{
|
||||
if (m_dtTypes.contains(topicName))
|
||||
return m_dtTypes.value(topicName);
|
||||
// may be this workaround should be changed in the future ... it is necessary for adding function topics via grad & drop
|
||||
return m_ftTypes.value(topicName);
|
||||
}
|
||||
|
||||
void NGLSettings::insertDataTopic(const TopicDescriptionEx &td)
|
||||
{
|
||||
m_dtTypes.insert(td.name, td);
|
||||
}
|
||||
|
||||
TopicDescriptionEx NGLSettings::topicFromTopicName(const std::string &topicName) const
|
||||
{
|
||||
TopicDescriptionEx td = functionTopic(topicName);
|
||||
if (!td.isEmpty())
|
||||
return td;
|
||||
return dataTopic(topicName);
|
||||
}
|
||||
|
||||
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Settings for zwmsqt
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <DDS/Generic/topic_description.h>
|
||||
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <QMap>
|
||||
|
||||
struct TopicDescriptionEx : public DDSGeneric::TopicDescription
|
||||
{
|
||||
TopicDescriptionEx() = default;
|
||||
explicit TopicDescriptionEx(const DDSGeneric::TopicDescription &td)
|
||||
: DDSGeneric::TopicDescription(td)
|
||||
{}
|
||||
QString serviceName;
|
||||
};
|
||||
|
||||
class NGLSettings : public NGL::Settings
|
||||
{
|
||||
NGLSettings();
|
||||
public:
|
||||
|
||||
// QString --> TopicName
|
||||
QMap<QString, QStringList> infoValues();
|
||||
void setInfoValues(const QMap<QString, QStringList> &map);
|
||||
|
||||
int ddsDomain() const;
|
||||
void setDdsDomain(int domainId);
|
||||
|
||||
static NGLSettings *instance();
|
||||
|
||||
/* Temporary "globally" accessible Values */
|
||||
// <TopicNAME, Description>
|
||||
QMap<std::string, TopicDescriptionEx> m_dtTypes;
|
||||
QMap<std::string, TopicDescriptionEx> m_ftTypes;
|
||||
|
||||
// DataTopics
|
||||
QMap<std::string, TopicDescriptionEx> dataTopics() const;
|
||||
bool hasDataTopic(const std::string &name) const;
|
||||
TopicDescriptionEx dataTopic(const std::string &topicName) const;
|
||||
TopicDescriptionEx dataTopic(const QString &topicName) const { return dataTopic(topicName.toStdString()); }
|
||||
void insertDataTopic(const TopicDescriptionEx &td);
|
||||
|
||||
// FunctionTopics
|
||||
QMap<std::string, TopicDescriptionEx> functionTopics() const;
|
||||
bool hasFunctionTopic(const std::string &name) const;
|
||||
TopicDescriptionEx functionTopic(const std::string &topicName) const;
|
||||
void insertFunctionTopic(const TopicDescriptionEx &td);
|
||||
|
||||
TopicDescriptionEx topicFromTopicName(const std::string &topicName) const;
|
||||
TopicDescriptionEx topicFromTopicName(const QString &topicName) const { return topicFromTopicName(topicName.toStdString()); }
|
||||
};
|
||||
|
||||
Executable
+357
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Widgets that change from Label to * if clicked (and not read only)
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "rwwidgets.h"
|
||||
#include <QStackedWidget>
|
||||
#include <QSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QCheckBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <LtsWidgets/lts_bitfield_widget.h>
|
||||
|
||||
void MyLabel::mousePressEvent(QMouseEvent *ev)
|
||||
{
|
||||
Q_UNUSED(ev)
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
|
||||
|
||||
ResizableStackedWidget::ResizableStackedWidget(QWidget *parent)
|
||||
: QStackedWidget(parent)
|
||||
{
|
||||
connect(this, &ResizableStackedWidget::currentChanged,
|
||||
this, &ResizableStackedWidget::onCurrentChanged);
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
}
|
||||
|
||||
int ResizableStackedWidget::addWigdet(QWidget *widget)
|
||||
{
|
||||
widget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||
return QStackedWidget::addWidget(widget);
|
||||
}
|
||||
|
||||
int ResizableStackedWidget::insertWidget(int index, QWidget *widget)
|
||||
{
|
||||
widget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||
return QStackedWidget::insertWidget(index, widget);
|
||||
}
|
||||
|
||||
void ResizableStackedWidget::onCurrentChanged(int index)
|
||||
{
|
||||
QWidget *w = widget(index);
|
||||
Q_ASSERT(w);
|
||||
w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
w->adjustSize();
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
|
||||
|
||||
RWWidget::RWWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_label(new MyLabel(this))
|
||||
, m_stackedWidget(new ResizableStackedWidget(this))
|
||||
, m_editableWidget(nullptr)
|
||||
{
|
||||
QVBoxLayout *l = new QVBoxLayout(this);
|
||||
l->setMargin(0);
|
||||
l->setSpacing(0);
|
||||
l->addWidget(m_stackedWidget);
|
||||
m_stackedWidget->insertWidget(0, m_label);
|
||||
|
||||
connect(m_label, &MyLabel::clicked, this, &RWWidget::onSetWritablePrivate);
|
||||
}
|
||||
|
||||
void RWWidget::setToolTip(const QString &string)
|
||||
{
|
||||
m_label->setToolTip(string);
|
||||
if (m_editableWidget)
|
||||
m_editableWidget->setToolTip(string);
|
||||
}
|
||||
|
||||
void RWWidget::onSetWritable()
|
||||
{
|
||||
if (m_editableWidget)
|
||||
m_stackedWidget->setCurrentWidget(m_editableWidget);
|
||||
}
|
||||
|
||||
void RWWidget::onSetWritablePrivate()
|
||||
{
|
||||
onSetWritable();
|
||||
if (m_editableWidget)
|
||||
emit writableRequested();
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
|
||||
|
||||
RWSpinBox::RWSpinBox(RwMode readOnly, QWidget *parent)
|
||||
: RWWidget(parent)
|
||||
, m_spinBox(readOnly == RwMode::ReadOnly ? nullptr : new QSpinBox(this))
|
||||
{
|
||||
if(!m_spinBox)
|
||||
return;
|
||||
|
||||
m_editableWidget = m_spinBox;
|
||||
m_spinBox->setMinimum(std::numeric_limits<int>::min());
|
||||
m_spinBox->setMaximum(std::numeric_limits<int>::max());
|
||||
m_stackedWidget->insertWidget(1, m_editableWidget);
|
||||
onSetWritable();
|
||||
}
|
||||
|
||||
void RWSpinBox::setMinimum(int min)
|
||||
{
|
||||
if (m_spinBox)
|
||||
m_spinBox->setMinimum(min);
|
||||
}
|
||||
|
||||
void RWSpinBox::setMaximum(int max)
|
||||
{
|
||||
if (m_spinBox)
|
||||
m_spinBox->setMaximum(max);
|
||||
}
|
||||
|
||||
void RWSpinBox::setValue(int val)
|
||||
{
|
||||
m_label->setText(QString::number(val));
|
||||
if (m_spinBox)
|
||||
m_spinBox->setValue(val);
|
||||
}
|
||||
|
||||
int RWSpinBox::value() const
|
||||
{
|
||||
return m_spinBox ? m_spinBox->value() : m_label->text().toInt();
|
||||
}
|
||||
|
||||
/*****************************************************/
|
||||
|
||||
|
||||
RWDoubleSpinBox::RWDoubleSpinBox(RwMode readOnly, QWidget *parent)
|
||||
: RWWidget(parent)
|
||||
, m_doubleSpinBox(readOnly == RwMode::ReadOnly ? nullptr : new QDoubleSpinBox(this))
|
||||
, m_precision(8)
|
||||
{
|
||||
if(!m_doubleSpinBox)
|
||||
return;
|
||||
|
||||
m_editableWidget = m_doubleSpinBox;
|
||||
m_stackedWidget->insertWidget(1, m_editableWidget);
|
||||
setDecimals(m_precision);
|
||||
connect(m_doubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RWDoubleSpinBox::valueChanged);
|
||||
onSetWritable();
|
||||
}
|
||||
|
||||
void RWDoubleSpinBox::setMinimum(double min)
|
||||
{
|
||||
if (m_doubleSpinBox)
|
||||
m_doubleSpinBox->setMinimum(min);
|
||||
}
|
||||
|
||||
void RWDoubleSpinBox::setMaximum(double max)
|
||||
{
|
||||
if (m_doubleSpinBox)
|
||||
m_doubleSpinBox->setMaximum(max);
|
||||
}
|
||||
|
||||
void RWDoubleSpinBox::setDecimals(int prec)
|
||||
{
|
||||
m_precision = prec;
|
||||
if (m_doubleSpinBox)
|
||||
m_doubleSpinBox->setDecimals(prec);
|
||||
m_label->setText(QString::number(m_label->text().toDouble(), 'f', prec));
|
||||
}
|
||||
|
||||
void RWDoubleSpinBox::setValue(double val)
|
||||
{
|
||||
m_label->setText(QString::number(val, 'f', m_precision));
|
||||
if (m_doubleSpinBox)
|
||||
m_doubleSpinBox->setValue(val);
|
||||
emit valueChanged();
|
||||
}
|
||||
|
||||
double RWDoubleSpinBox::value() const
|
||||
{
|
||||
return m_doubleSpinBox ? m_doubleSpinBox->value() : m_label->text().toDouble();
|
||||
}
|
||||
|
||||
/*****************************************************/
|
||||
|
||||
|
||||
RWCheckBox::RWCheckBox(RwMode readOnly, QWidget *parent)
|
||||
: RWWidget(parent)
|
||||
, m_checkBox(readOnly == RwMode::ReadOnly ? nullptr : new QCheckBox(this))
|
||||
{
|
||||
if(!m_checkBox)
|
||||
return;
|
||||
|
||||
m_editableWidget = m_checkBox;
|
||||
m_stackedWidget->insertWidget(1, m_editableWidget);
|
||||
onSetWritable();
|
||||
}
|
||||
|
||||
void RWCheckBox::setChecked(bool val)
|
||||
{
|
||||
m_label->setText(val ? tr("Yes") : tr("No"));
|
||||
if (m_checkBox)
|
||||
m_checkBox->setChecked(val);
|
||||
}
|
||||
|
||||
bool RWCheckBox::isChecked() const
|
||||
{
|
||||
return m_checkBox ? m_checkBox->isChecked() : (m_label->text() == tr("Yes"));
|
||||
}
|
||||
|
||||
/*****************************************************/
|
||||
|
||||
|
||||
RWLineEdit::RWLineEdit(RwMode readOnly, QWidget *parent)
|
||||
: RWWidget(parent)
|
||||
, m_lineEdit(readOnly == RwMode::ReadOnly ? nullptr : new QLineEdit(this))
|
||||
{
|
||||
if(!m_lineEdit)
|
||||
return;
|
||||
|
||||
m_editableWidget = m_lineEdit;
|
||||
m_stackedWidget->insertWidget(1, m_editableWidget);
|
||||
onSetWritable();
|
||||
}
|
||||
|
||||
void RWLineEdit::setMaxLength(int length)
|
||||
{
|
||||
if (m_lineEdit)
|
||||
m_lineEdit->setMaxLength(length);
|
||||
}
|
||||
|
||||
void RWLineEdit::setText(const QString &val)
|
||||
{
|
||||
m_label->setText(val);
|
||||
if (m_lineEdit)
|
||||
m_lineEdit->setText(val);
|
||||
}
|
||||
|
||||
QString RWLineEdit::text() const
|
||||
{
|
||||
return m_lineEdit ? m_lineEdit->text() : m_label->text();
|
||||
}
|
||||
|
||||
/*****************************************************/
|
||||
|
||||
|
||||
RWComboBox::RWComboBox(RwMode readOnly, QWidget *parent)
|
||||
: RWWidget(parent)
|
||||
, m_comboBox(readOnly == RwMode::ReadOnly ? nullptr : new QComboBox(this))
|
||||
{
|
||||
if(!m_comboBox)
|
||||
return;
|
||||
|
||||
m_editableWidget = m_comboBox;
|
||||
m_stackedWidget->insertWidget(1, m_editableWidget);
|
||||
onSetWritable();
|
||||
}
|
||||
|
||||
void RWComboBox::addItem(const QString &text, int ordinal)
|
||||
{
|
||||
if (m_comboBox)
|
||||
m_comboBox->addItem(text, ordinal);
|
||||
m_values.insert(ordinal, text);
|
||||
}
|
||||
|
||||
void RWComboBox::insertItem(int index, const QString &text, int ordinal)
|
||||
{
|
||||
if (m_comboBox)
|
||||
m_comboBox->insertItem(index, text, ordinal);
|
||||
m_values.insert(ordinal, text);
|
||||
}
|
||||
|
||||
int RWComboBox::count() const
|
||||
{
|
||||
return (m_comboBox ? m_comboBox->count() : 0);
|
||||
}
|
||||
|
||||
//int RWComboBox::currentIndex() const
|
||||
//{
|
||||
// return (m_comboBox ? m_comboBox->currentIndex() : 0);
|
||||
//}
|
||||
|
||||
int RWComboBox::currentValue() const
|
||||
{
|
||||
return (m_comboBox ? m_comboBox->itemData(m_comboBox->currentIndex()).toInt() : m_values.key(m_label->text()));
|
||||
}
|
||||
|
||||
QString RWComboBox::currentText() const
|
||||
{
|
||||
return (m_comboBox ? m_comboBox->currentText() : m_label->text());
|
||||
}
|
||||
|
||||
//void RWComboBox::setCurrentIndex(int index)
|
||||
//{
|
||||
// if (m_comboBox)
|
||||
// {
|
||||
// m_comboBox->setCurrentIndex(index);
|
||||
// m_label->setText(m_comboBox->currentText());
|
||||
// }
|
||||
// // Impossible for readOnly! Map is not indexed...
|
||||
//}
|
||||
|
||||
void RWComboBox::setCurrentValue(int ordinal)
|
||||
{
|
||||
if (m_comboBox)
|
||||
m_comboBox->setCurrentIndex(m_comboBox->findData(ordinal));
|
||||
m_label->setText(m_values.value(ordinal));
|
||||
}
|
||||
|
||||
/*****************************************************/
|
||||
|
||||
RWBitfieldWidget::RWBitfieldWidget(RwMode readOnly, QWidget *parent)
|
||||
: RWWidget(parent)
|
||||
, m_bitfield(readOnly == RwMode::ReadOnly ? nullptr : new LtsWidgets::LtsBitfieldWidget(parent, 0))
|
||||
{
|
||||
if(!m_bitfield)
|
||||
return;
|
||||
|
||||
m_editableWidget = m_bitfield;
|
||||
m_stackedWidget->insertWidget(1, m_editableWidget);
|
||||
connect(m_bitfield, &LtsWidgets::LtsBitfieldWidget::valueChanged , this, &RWBitfieldWidget::valueChanged);
|
||||
connect(m_bitfield, &LtsWidgets::LtsBitfieldWidget::hasCaptionChanged, this, &RWBitfieldWidget::hasCaptionChanged);
|
||||
onSetWritable();
|
||||
}
|
||||
|
||||
void RWBitfieldWidget::setValue(quint8 val)
|
||||
{
|
||||
m_label->setText(QLatin1Literal("0b") + QString::number(val,2));
|
||||
if (m_bitfield)
|
||||
m_bitfield->setValue(val);
|
||||
}
|
||||
|
||||
quint8 RWBitfieldWidget::value()
|
||||
{
|
||||
if(!m_bitfield)
|
||||
return m_label->text().remove(0,2).toUInt(nullptr,2);
|
||||
|
||||
return m_bitfield->value();
|
||||
}
|
||||
|
||||
void RWBitfieldWidget::setHasCaption(bool option)
|
||||
{
|
||||
if (m_bitfield)
|
||||
m_bitfield->setHasCaption(option);
|
||||
}
|
||||
|
||||
bool RWBitfieldWidget::hasCaption()
|
||||
{
|
||||
return m_bitfield ? m_bitfield->hasCaption() : false;
|
||||
}
|
||||
|
||||
|
||||
Executable
+202
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Widgets that change from QLabel to Q* if clicked (and not read only)
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QStackedWidget>
|
||||
#include <QMap>
|
||||
|
||||
class QSpinBox;
|
||||
class QDoubleSpinBox;
|
||||
class QCheckBox;
|
||||
class QLineEdit;
|
||||
class QComboBox;
|
||||
|
||||
namespace LtsWidgets {
|
||||
class LtsBitfieldWidget;
|
||||
}
|
||||
|
||||
enum class RwMode {
|
||||
ReadOnly,
|
||||
WriteOnly,
|
||||
ReadWrite
|
||||
};
|
||||
|
||||
class MyLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using QLabel::QLabel;
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
|
||||
};
|
||||
|
||||
class ResizableStackedWidget : public QStackedWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ResizableStackedWidget(QWidget *parent = nullptr);
|
||||
|
||||
int addWigdet(QWidget *widget);
|
||||
int insertWidget(int index, QWidget *widget);
|
||||
|
||||
private slots:
|
||||
void onCurrentChanged(int index);
|
||||
|
||||
};
|
||||
|
||||
class RWWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RWWidget(QWidget *parent = nullptr);
|
||||
|
||||
void setToolTip(const QString &string);
|
||||
|
||||
public slots:
|
||||
void onSetWritable();
|
||||
|
||||
private slots:
|
||||
void onSetWritablePrivate();
|
||||
|
||||
signals:
|
||||
void writableRequested();
|
||||
|
||||
protected:
|
||||
MyLabel *m_label;
|
||||
ResizableStackedWidget *m_stackedWidget;
|
||||
QWidget *m_editableWidget;
|
||||
|
||||
};
|
||||
|
||||
class RWSpinBox : public RWWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RWSpinBox(RwMode readOnly = RwMode::ReadWrite, QWidget *parent = nullptr);
|
||||
|
||||
void setMinimum(int min);
|
||||
void setMaximum(int max);
|
||||
|
||||
void setValue(int val);
|
||||
int value() const;
|
||||
|
||||
private:
|
||||
QSpinBox *m_spinBox;
|
||||
|
||||
};
|
||||
|
||||
class RWDoubleSpinBox : public RWWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RWDoubleSpinBox(RwMode readOnly = RwMode::ReadWrite, QWidget *parent = nullptr);
|
||||
|
||||
void setMinimum(double min);
|
||||
void setMaximum(double max);
|
||||
void setDecimals(int prec);
|
||||
|
||||
void setValue(double val);
|
||||
double value() const;
|
||||
|
||||
signals:
|
||||
void valueChanged();
|
||||
private:
|
||||
QDoubleSpinBox *m_doubleSpinBox;
|
||||
int m_precision;
|
||||
|
||||
};
|
||||
|
||||
class RWCheckBox : public RWWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RWCheckBox(RwMode readOnly = RwMode::ReadWrite, QWidget *parent = nullptr);
|
||||
|
||||
void setChecked(bool val);
|
||||
bool isChecked() const;
|
||||
|
||||
private:
|
||||
QCheckBox *m_checkBox;
|
||||
|
||||
};
|
||||
|
||||
class RWLineEdit : public RWWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RWLineEdit(RwMode readOnly = RwMode::ReadWrite, QWidget *parent = nullptr);
|
||||
|
||||
void setMaxLength(int length);
|
||||
|
||||
void setText(const QString &text);
|
||||
QString text() const;
|
||||
|
||||
private:
|
||||
QLineEdit *m_lineEdit;
|
||||
|
||||
};
|
||||
|
||||
class RWComboBox : public RWWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RWComboBox(RwMode readOnly = RwMode::ReadWrite, QWidget *parent = nullptr);
|
||||
|
||||
void addItem(const QString &text, int ordinal = -1);
|
||||
void insertItem(int index, const QString &text, int ordinal = -1);
|
||||
int count() const;
|
||||
// int currentIndex() const; //Shouldn't be used...
|
||||
int currentValue() const;
|
||||
QString currentText() const;
|
||||
// void setCurrentIndex(int index); //Shouldn't be used...
|
||||
void setCurrentValue(int ordinal);
|
||||
|
||||
|
||||
private:
|
||||
QComboBox *m_comboBox;
|
||||
QMap<int, QString> m_values; // For ReadOnly without ComboBox
|
||||
|
||||
};
|
||||
|
||||
class RWBitfieldWidget : public RWWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RWBitfieldWidget(RwMode readOnly = RwMode::ReadWrite, QWidget *parent = nullptr);
|
||||
|
||||
void setValue(quint8 val);
|
||||
quint8 value();
|
||||
|
||||
void setHasCaption(bool option);
|
||||
bool hasCaption();
|
||||
|
||||
signals:
|
||||
void valueChanged(quint8 newValue);
|
||||
void hasCaptionChanged(bool option);
|
||||
|
||||
private:
|
||||
LtsWidgets::LtsBitfieldWidget *m_bitfield;
|
||||
};
|
||||
Executable
+534
@@ -0,0 +1,534 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableModel for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "samplemodel.h"
|
||||
#include <QMimeData>
|
||||
#include <QStringList>
|
||||
#include <QDomDocument>
|
||||
#include "detailviewdlg.h"
|
||||
#include "nglsettings.h"
|
||||
#include "xmlhelper.h"
|
||||
|
||||
//
|
||||
// SampleModel::SampleData
|
||||
//
|
||||
QByteArray SampleModel::SampleData::encodeForMime() const
|
||||
{
|
||||
QByteArray encodedData;
|
||||
QDataStream stream(&encodedData, QIODevice::WriteOnly);
|
||||
stream << QString::fromStdString(typeName)
|
||||
<< reinterpret_cast<quint64>(legatusData.data())
|
||||
<< topicName
|
||||
<< description
|
||||
<< pause;
|
||||
|
||||
return encodedData;
|
||||
}
|
||||
|
||||
SampleModel::SampleData SampleModel::SampleData::decodeFromMime(const QByteArray &encodedData)
|
||||
{
|
||||
SampleData ret;
|
||||
QDataStream stream(const_cast<QByteArray *>(&encodedData), QIODevice::ReadOnly);
|
||||
QString tn;
|
||||
quint64 ptr;
|
||||
dds::core::xtypes::DynamicData *dynDataPtr;
|
||||
stream >> tn >> ptr >> ret.topicName >> ret.description >> ret.pause;
|
||||
ret.typeName = tn.toStdString();
|
||||
dynDataPtr = reinterpret_cast<dds::core::xtypes::DynamicData *>(ptr);
|
||||
ret.legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(new dds::core::xtypes::DynamicData(*dynDataPtr));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//
|
||||
// SampleModel
|
||||
//
|
||||
SampleModel::SampleModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_logger(DdsView::getLogger("SampleModel"))
|
||||
{}
|
||||
|
||||
SampleModel::~SampleModel()
|
||||
{
|
||||
for (const SampleData &sd : qAsConst(m_data))
|
||||
delete sd.dialog;
|
||||
}
|
||||
|
||||
int SampleModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
int SampleModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return Column::Maximum + 1;
|
||||
}
|
||||
|
||||
QVariant SampleModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (index.column() >= columnCount(QModelIndex()) ||
|
||||
index.row() >= rowCount(QModelIndex()))
|
||||
return QVariant();
|
||||
|
||||
const SampleData &data = m_data[index.row()];
|
||||
|
||||
if (role == Qt::ToolTipRole
|
||||
&& index.column() == Column::TopicName)
|
||||
return QString::fromStdString(data.typeName);
|
||||
|
||||
if (role != Qt::DisplayRole &&
|
||||
role != Qt::EditRole)
|
||||
return QVariant();
|
||||
|
||||
if (data.typeName.empty())
|
||||
return QVariant();
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case Column::TopicName:
|
||||
return data.topicName;
|
||||
case Column::Description:
|
||||
return data.description;
|
||||
case Column::TypeName:
|
||||
return QString::fromStdString(data.typeName);
|
||||
case Column::Pause:
|
||||
return data.pause;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool SampleModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!index.isValid() ||
|
||||
role != Qt::EditRole)
|
||||
return false;
|
||||
|
||||
if (index.column() >= columnCount(QModelIndex()) ||
|
||||
index.row() >= rowCount(QModelIndex()))
|
||||
return false;
|
||||
|
||||
SampleData &data = m_data[index.row()];
|
||||
|
||||
bool retVal = false;
|
||||
switch (index.column())
|
||||
{
|
||||
// Non editable!
|
||||
case Column::TopicName:
|
||||
case Column::TypeName:
|
||||
break;
|
||||
case Column::Description:
|
||||
data.description = value.toString();
|
||||
retVal = true;
|
||||
if (data.dialog)
|
||||
data.dialog->setWindowTitle(QString::fromLatin1("%1 (%2)").arg(data.description, data.topicName));
|
||||
break;
|
||||
case Column::Pause:
|
||||
{
|
||||
int i = value.toInt(&retVal);
|
||||
if (retVal)
|
||||
data.pause = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (retVal)
|
||||
emit dataChanged(index, index);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool SampleModel::updateRow(const QModelIndex &index,
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> &data,
|
||||
const QString &topicName)
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= rowCount(QModelIndex()))
|
||||
return false;
|
||||
|
||||
SampleData &sample = m_data[row];
|
||||
if (sample.legatusData->type() != data->type())
|
||||
return false;
|
||||
|
||||
sample.legatusData = data;
|
||||
sample.topicName = topicName;
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::DropActions SampleModel::supportedDropActions() const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
}
|
||||
|
||||
Qt::DropActions SampleModel::supportedDragActions() const
|
||||
{
|
||||
return Qt::MoveAction | Qt::CopyAction;
|
||||
}
|
||||
|
||||
QVariant SampleModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case Column::TopicName:
|
||||
return tr("Topic Name");
|
||||
case Column::Description:
|
||||
return tr("Description");
|
||||
case Column::TypeName:
|
||||
return tr("Type Name");
|
||||
case Column::Pause:
|
||||
return tr("Pause");
|
||||
}
|
||||
}
|
||||
return QAbstractTableModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
std::string SampleModel::typeName(const QModelIndex &index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_data.size())
|
||||
return std::string();
|
||||
return m_data[row].typeName;
|
||||
}
|
||||
|
||||
bool SampleModel::addRow(const SampleData &sample, int position)
|
||||
{
|
||||
if (position < 0 || position > m_data.size())
|
||||
position = m_data.size();
|
||||
|
||||
beginInsertRows(QModelIndex(), position, position);
|
||||
m_data.insert(position, sample);
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SampleModel::deleteRow(const QModelIndex &index)
|
||||
{
|
||||
int position = index.row();
|
||||
if (position < 0 || position > m_data.size())
|
||||
return false;
|
||||
|
||||
beginRemoveRows(QModelIndex(), position, position);
|
||||
delete m_data[position].dialog;
|
||||
m_data.remove(position);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SampleModel::onClear()
|
||||
{
|
||||
beginResetModel();
|
||||
auto it = m_data.begin();
|
||||
while (it != m_data.end())
|
||||
{
|
||||
delete (*it).dialog;
|
||||
it = m_data.erase(it);
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
Qt::ItemFlags SampleModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags flags = QAbstractTableModel::flags(index) | Qt::ItemIsDropEnabled;
|
||||
|
||||
if (index.isValid())
|
||||
flags |= Qt::ItemIsDragEnabled;
|
||||
|
||||
if (index.column() == Column::Description || index.column() == Column::Pause)
|
||||
flags |= Qt::ItemIsEditable;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
QMimeData *SampleModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
|
||||
QModelIndex mi = indexes.value(0);
|
||||
if (!mi.isValid())
|
||||
return mimeData;
|
||||
|
||||
const SampleData &sample = m_data[mi.row()];
|
||||
|
||||
mimeData->setData(QLatin1String("SampleModel/SampleData"), sample.encodeForMime());
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
bool SampleModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
|
||||
int row, int column, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(column)
|
||||
if (action == Qt::IgnoreAction)
|
||||
return true;
|
||||
|
||||
if ((action != Qt::MoveAction) && (action != Qt::CopyAction))
|
||||
return false;
|
||||
|
||||
if (data->hasFormat(QLatin1String("SampleModel/SampleData")))
|
||||
{
|
||||
const QByteArray encodedData = data->data(QLatin1String("SampleModel/SampleData"));
|
||||
SampleData sample = SampleData::decodeFromMime(encodedData);
|
||||
|
||||
int beginRow;
|
||||
if (row != -1)
|
||||
beginRow = row;
|
||||
else if (parent.isValid())
|
||||
beginRow = parent.row();
|
||||
else
|
||||
beginRow = rowCount(QModelIndex());
|
||||
|
||||
addRow(sample, beginRow);
|
||||
}
|
||||
else if (data->hasFormat(QLatin1String("dds::core::xtypes::DynamicData/Pointer+TopicName")))
|
||||
{
|
||||
QByteArray encodedData = data->data(QLatin1String("dds::core::xtypes::DynamicData/Pointer+TopicName"));
|
||||
QDataStream stream(&encodedData, QIODevice::ReadOnly);
|
||||
while (!stream.atEnd())
|
||||
{
|
||||
SampleData sample;
|
||||
quint64 ptrVal;
|
||||
|
||||
stream >> ptrVal;
|
||||
stream >> sample.topicName;
|
||||
|
||||
dds::core::xtypes::DynamicData *ptr = reinterpret_cast<dds::core::xtypes::DynamicData *>(ptrVal);
|
||||
if (ptr->type_kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
continue;
|
||||
|
||||
sample.legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(new dds::core::xtypes::DynamicData(*ptr));
|
||||
sample.typeName = ptr->type().name();
|
||||
sample.description = sample.topicName;
|
||||
|
||||
int beginRow;
|
||||
if (row != -1)
|
||||
beginRow = row;
|
||||
else if (parent.isValid())
|
||||
beginRow = parent.row();
|
||||
else
|
||||
beginRow = rowCount(QModelIndex());
|
||||
|
||||
addRow(sample, beginRow);
|
||||
}
|
||||
}
|
||||
else if (data->hasFormat(QLatin1String("Text/TopicName+TypeName")))
|
||||
{
|
||||
QByteArray topicName, typeName;
|
||||
QByteArray encodedData = data->data(QLatin1String("Text/TopicName+TypeName"));
|
||||
QDataStream stream(&encodedData, QIODevice::ReadOnly);
|
||||
stream >> topicName;
|
||||
stream >> typeName;
|
||||
|
||||
SampleData sample;
|
||||
|
||||
// TODO: set topicName
|
||||
sample.typeName = typeName.toStdString();
|
||||
sample.topicName = QString::fromLatin1(topicName.data());
|
||||
sample.description = sample.topicName;
|
||||
sample.legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(new dds::core::xtypes::DynamicData(NGLSettings::instance()->dataTopic(sample.topicName).type));
|
||||
|
||||
int beginRow;
|
||||
if (row != -1)
|
||||
beginRow = row;
|
||||
else if (parent.isValid())
|
||||
beginRow = parent.row();
|
||||
else
|
||||
beginRow = rowCount(QModelIndex());
|
||||
|
||||
addRow(sample, beginRow);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList SampleModel::mimeTypes() const
|
||||
{
|
||||
QStringList sl;
|
||||
sl << QLatin1String("SampleModel/SampleData"); // (SampleModel::SampleData)
|
||||
sl << QLatin1String("dds::core::xtypes::DynamicData/Pointer+TopicName"); // (dds::core::xtypes::DynamicData*) & topicName
|
||||
sl << QLatin1String("Text/TopicName+TypeName"); // e.g. "LTS_ExerciseParticipant" & "ParticipantManagement::ExerciseParticipant"
|
||||
return sl;
|
||||
}
|
||||
|
||||
bool SampleModel::addRow(const dds::core::xtypes::DynamicData &data, const QString &topicName, const QString &description)
|
||||
{
|
||||
SampleData id;
|
||||
|
||||
id.typeName = data.type().name();
|
||||
id.topicName = topicName;
|
||||
id.description = description.isEmpty() ? topicName : description;
|
||||
id.legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(new dds::core::xtypes::DynamicData(data.type()));
|
||||
|
||||
return addRow(id);
|
||||
}
|
||||
|
||||
SampleModel::SampleData SampleModel::sampleData(const QModelIndex &index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_data.size())
|
||||
return SampleData();
|
||||
SampleData data = m_data.value(row);
|
||||
//DEBUG_LOG(m_logger) << "intern: " << data.legatusData.data()->type().name();
|
||||
return data;
|
||||
}
|
||||
|
||||
void SampleModel::setDialog(const QModelIndex &index, DetailViewDlg *dlg)
|
||||
{
|
||||
// Dialog will not be destructed! It will be hidden and reused on second usage.
|
||||
// ~SampleModel takes care tp destruct anyways.
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_data.size())
|
||||
return;
|
||||
m_data[row].dialog = dlg;
|
||||
}
|
||||
|
||||
QModelIndex SampleModel::indexByDialog(const DetailViewDlg *dlg) const
|
||||
{
|
||||
for (int i = 0; i < m_data.size(); ++i)
|
||||
if (m_data[i].dialog == dlg)
|
||||
return index(i, 0);
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
//
|
||||
// SampleSortModel
|
||||
//
|
||||
bool SampleSortModel::lessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const
|
||||
{
|
||||
if (left.column() != right.column())
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
SampleModel *srcModel = qobject_cast<SampleModel *>(sourceModel());
|
||||
if (!srcModel)
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
SampleModel::SampleData dataLeft = srcModel->sampleData(left);
|
||||
SampleModel::SampleData dataRight = srcModel->sampleData(right);
|
||||
switch (left.column())
|
||||
{
|
||||
case SampleModel::Column::TopicName:
|
||||
return dataLeft.topicName < dataRight.topicName;
|
||||
case SampleModel::Column::Description:
|
||||
return dataLeft.description < dataRight.description;
|
||||
case SampleModel::Column::TypeName:
|
||||
return dataLeft.typeName < dataRight.typeName;
|
||||
case SampleModel::Column::Pause:
|
||||
return dataLeft.pause < dataRight.pause;
|
||||
}
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
|
||||
void SampleSortModel::sort(int column, Qt::SortOrder order)
|
||||
{
|
||||
QModelIndex ind = index(m_currentRow, 0);
|
||||
int sourceRow = mapToSource(ind).row();
|
||||
QSortFilterProxyModel::sort(column, order);
|
||||
QModelIndex dex = sourceModel()->index(sourceRow, 0);
|
||||
m_currentRow = mapFromSource(dex).row();
|
||||
emit headerDataChanged(Qt::Vertical, m_currentRow, m_currentRow);
|
||||
}
|
||||
|
||||
QVariant SampleSortModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
return QSortFilterProxyModel::headerData(section, orientation, role);
|
||||
|
||||
if (orientation == Qt::Vertical)
|
||||
{
|
||||
if (section == m_currentRow)
|
||||
return QChar(0x21D2);
|
||||
return QString();
|
||||
}
|
||||
return sourceModel()->headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
void SampleModel::SampleData::toXml(QDomDocument &doc, QDomElement &elem) const
|
||||
{
|
||||
QDomElement deSample = doc.createElement(QLatin1String("Sample"));
|
||||
elem.appendChild(deSample);
|
||||
|
||||
QDomElement deTopicName = doc.createElement(QLatin1String("TopicName"));
|
||||
deSample.appendChild(deTopicName);
|
||||
QDomText deTopicNameContents = doc.createTextNode(topicName);
|
||||
deTopicName.appendChild(deTopicNameContents);
|
||||
|
||||
QDomElement deTypeName = doc.createElement(QLatin1String("TypeName"));
|
||||
deSample.appendChild(deTypeName);
|
||||
QDomText deTypeNameContents = doc.createTextNode(QString::fromStdString(typeName));
|
||||
deTypeName.appendChild(deTypeNameContents);
|
||||
|
||||
QDomElement dePause = doc.createElement(QLatin1String("Pause"));
|
||||
deSample.appendChild(dePause);
|
||||
QDomText dePauseContents = doc.createTextNode(QString::number(pause));
|
||||
dePause.appendChild(dePauseContents);
|
||||
|
||||
XmlHelper::dds2Xml(*legatusData, topicName, doc, deSample);
|
||||
}
|
||||
|
||||
bool SampleModel::SampleData::fromXml(QDomElement &elem)
|
||||
{
|
||||
if (elem.nodeName().compare(QLatin1String("Sample"), Qt::CaseInsensitive) != 0)
|
||||
return false;
|
||||
|
||||
bool succ = false;
|
||||
QDomNodeList dnl = elem.childNodes();
|
||||
for (int i = 0; i < dnl.size(); ++i)
|
||||
{
|
||||
QDomNode dn = dnl.at(i);
|
||||
if (!dn.isElement())
|
||||
continue;
|
||||
QDomElement de = dn.toElement();
|
||||
if (de.nodeName() == QLatin1String("TopicName"))
|
||||
topicName = de.text();
|
||||
else if (de.nodeName() == QLatin1String("Type"))
|
||||
typeName = de.text().toStdString();
|
||||
else if (de.nodeName() == QLatin1String("Pause"))
|
||||
{
|
||||
bool ok;
|
||||
pause = de.text().toInt(&ok);
|
||||
if (!ok)
|
||||
pause = 1000;
|
||||
}
|
||||
else if (de.nodeName() == QLatin1String("DdsData"))
|
||||
{
|
||||
const QString topicName = XmlHelper::topicNameFromXml(de);
|
||||
const TopicDescriptionEx td = NGLSettings::instance()->topicFromTopicName(topicName);
|
||||
const dds::core::xtypes::DynamicData data = XmlHelper::xml2Dds(de, td);
|
||||
dds::core::xtypes::TypeKind kind = dds::core::xtypes::TypeKind::PRIMITIVE_TYPE;
|
||||
try
|
||||
{
|
||||
kind = data.type_kind();
|
||||
}
|
||||
catch (dds::core::Exception &e)
|
||||
{
|
||||
qWarning("dds exception: %s", e.what());
|
||||
continue;
|
||||
}
|
||||
if (kind != dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
continue;
|
||||
|
||||
legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(new dds::core::xtypes::DynamicData(data));
|
||||
typeName = data.type().name();
|
||||
succ = (typeName != "Unknown");
|
||||
}
|
||||
}
|
||||
if (topicName.isEmpty())
|
||||
topicName = QString::fromStdString(typeName);
|
||||
if (description.isEmpty())
|
||||
description = topicName;
|
||||
|
||||
return succ;
|
||||
}
|
||||
Executable
+114
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableModel for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
#include <QDomDocument>
|
||||
|
||||
#include <dds/dds.hpp>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
class SampleSortModel;
|
||||
class DetailViewDlg;
|
||||
|
||||
class SampleModel final : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SampleModel(QObject *parent = nullptr);
|
||||
~SampleModel();
|
||||
|
||||
enum Column
|
||||
{
|
||||
TypeName = 0,
|
||||
TopicName = 1,
|
||||
Description = 2,
|
||||
Pause = 3,
|
||||
Maximum = Pause
|
||||
};
|
||||
|
||||
struct SampleData
|
||||
{
|
||||
std::string typeName;
|
||||
QString topicName;
|
||||
QString description;
|
||||
int pause = 1000;
|
||||
QSharedPointer<dds::core::xtypes::DynamicData> legatusData;
|
||||
DetailViewDlg *dialog = nullptr;
|
||||
void toXml(QDomDocument &doc, QDomElement &elem) const;
|
||||
bool fromXml(QDomElement &elem);
|
||||
|
||||
QByteArray encodeForMime() const;
|
||||
static SampleData decodeFromMime(const QByteArray &encodedData);
|
||||
};
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
std::string typeName(const QModelIndex &index) const;
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> dynamicData(const QModelIndex &index) const;
|
||||
QString name(const QModelIndex &index) const;
|
||||
|
||||
Qt::DropActions supportedDropActions() const override;
|
||||
Qt::DropActions supportedDragActions() const override;
|
||||
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||
QStringList mimeTypes() const override;
|
||||
|
||||
bool addRow(const dds::core::xtypes::DynamicData &data, const QString &topicName, const QString &description = QString());
|
||||
bool deleteRow(const QModelIndex &index);
|
||||
bool updateRow(const QModelIndex &index, const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName);
|
||||
bool addRow(const SampleData &sample, int position = -1);
|
||||
|
||||
SampleData sampleData(const QModelIndex &index) const;
|
||||
void setDialog(const QModelIndex &index, DetailViewDlg *dlg);
|
||||
QModelIndex indexByDialog(const DetailViewDlg *dlg) const;
|
||||
|
||||
public slots:
|
||||
void onClear();
|
||||
|
||||
private:
|
||||
|
||||
QVector<SampleData> m_data;
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
friend class SampleSortModel;
|
||||
};
|
||||
|
||||
class SampleSortModel final : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QSortFilterProxyModel::QSortFilterProxyModel;
|
||||
|
||||
void setCurrentRow(int row) { m_currentRow = row; emit headerDataChanged(Qt::Vertical, row, row);}
|
||||
int currentRow() { return m_currentRow >= rowCount() ? -1 : m_currentRow; }
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
void sort(int column, Qt::SortOrder order) override;
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const override;
|
||||
private:
|
||||
int m_currentRow = -1;
|
||||
|
||||
};
|
||||
Executable
+354
@@ -0,0 +1,354 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableView for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "sampleview.h"
|
||||
#include "samplemodel.h"
|
||||
|
||||
#include "topicselector.h"
|
||||
#include "detailviewdlg.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
SampleView::SampleView(QWidget *parent)
|
||||
: QTableView(parent)
|
||||
, m_proxyModel(new SampleSortModel(this))
|
||||
, m_model(new SampleModel(this))
|
||||
, m_topicSelector(nullptr)
|
||||
, m_playTimer(this)
|
||||
, m_repeat(false)
|
||||
, m_bConnected(false)
|
||||
, m_logger(DdsView::getLogger("SampleView"))
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void SampleView::init()
|
||||
{
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
setModel(m_proxyModel);
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
|
||||
setSelectionBehavior(SelectRows);
|
||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
setSortingEnabled(true);
|
||||
horizontalHeader()->setStretchLastSection(true);
|
||||
setAcceptDrops(true);
|
||||
setDragEnabled(true);
|
||||
setDragDropMode(QAbstractItemView::DragDrop);
|
||||
setDropIndicatorShown(true);
|
||||
setDefaultDropAction(Qt::MoveAction);
|
||||
setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
|
||||
// verticalHeader()->hide();
|
||||
|
||||
connect(this, &SampleView::doubleClicked, this, &SampleView::onTableDoubleClicked);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &SampleView::customContextMenuRequested, this, &SampleView::onCustomContextMenuRequested);
|
||||
|
||||
connect(&m_playTimer, &QTimer::timeout, this, [this]() { onPlayTimerTimeout(); });
|
||||
m_playTimer.setSingleShot(true);
|
||||
// Todo: Model vs. ProxyModel? Column always correct?
|
||||
hideColumn(static_cast<int>(SampleModel::Column::TypeName));
|
||||
}
|
||||
|
||||
SampleView::~SampleView()
|
||||
{
|
||||
delete m_model;
|
||||
delete m_proxyModel;
|
||||
}
|
||||
|
||||
int SampleView::currentRow() const
|
||||
{
|
||||
return m_proxyModel->currentRow();
|
||||
}
|
||||
|
||||
void SampleView::setCurrentRow(int row)
|
||||
{
|
||||
m_proxyModel->setCurrentRow(row);
|
||||
}
|
||||
|
||||
void SampleView::onLoadSampleList()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Load Sample List"), QString(), tr("Sample List Files (*.spl);;All Files(*)"));
|
||||
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
QFile f(filename);
|
||||
if (!f.open(QFile::ReadOnly))
|
||||
{
|
||||
QMessageBox::warning(this, tr("File not readable"), tr("The selected file is not readable"));
|
||||
return;
|
||||
}
|
||||
QDomDocument doc;
|
||||
if (!doc.setContent(&f))
|
||||
{
|
||||
QMessageBox::warning(this, tr("File not readable"), tr("The selected file is no valid XML"));
|
||||
f.close();
|
||||
return;
|
||||
}
|
||||
f.close();
|
||||
|
||||
m_model->onClear();
|
||||
|
||||
QDomElement de = doc.documentElement();
|
||||
if (de.nodeName() != QLatin1String("Samples"))
|
||||
{
|
||||
QMessageBox::warning(this, tr("File not readable"), tr("The XML file has no valid Root Item."));
|
||||
return;
|
||||
}
|
||||
QDomNodeList dnl = de.childNodes();
|
||||
int errCount = 0;
|
||||
for (int i = 0; i < dnl.size(); ++i)
|
||||
{
|
||||
QDomNode dn = dnl.at(i);
|
||||
if (!dn.isElement())
|
||||
continue;
|
||||
QDomElement innerDe = dn.toElement();
|
||||
if (innerDe.nodeName() != QLatin1String("Sample"))
|
||||
continue;
|
||||
SampleModel::SampleData sample;
|
||||
bool succ = sample.fromXml(innerDe);
|
||||
if (succ)
|
||||
m_model->addRow(sample);
|
||||
else
|
||||
++errCount;
|
||||
}
|
||||
if (errCount > 0)
|
||||
QMessageBox::warning(this,
|
||||
tr("Not all samples could be read"),
|
||||
tr("There were %1 samples that could not be read from file.").arg(errCount));
|
||||
}
|
||||
|
||||
void SampleView::onSaveSampleList()
|
||||
{
|
||||
QString selectedFilter;
|
||||
QString filename = QFileDialog::getSaveFileName(this, tr("Save Sample List"), QString(),
|
||||
tr("Sample List Files (*.spl);;All Files(*)"),
|
||||
&selectedFilter);
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
if (selectedFilter.contains(QLatin1String("spl")) && !filename.endsWith(QLatin1String(".spl")))
|
||||
filename += QLatin1String(".spl");
|
||||
|
||||
QFile f(filename);
|
||||
if (!f.open(QFile::WriteOnly))
|
||||
return;
|
||||
QDomDocument doc(QLatin1String("Samples"));
|
||||
QDomElement root = doc.createElement(QLatin1String("Samples"));
|
||||
doc.appendChild(root);
|
||||
for (int i = 0; i < m_proxyModel->rowCount(); ++i)
|
||||
{
|
||||
QModelIndex mi = m_proxyModel->index(i, 0);
|
||||
QModelIndex sourceMi = m_proxyModel->mapToSource(mi);
|
||||
SampleModel::SampleData sample = m_model->sampleData(sourceMi);
|
||||
sample.toXml(doc, root);
|
||||
}
|
||||
f.write(doc.toByteArray());
|
||||
f.close();
|
||||
}
|
||||
|
||||
void SampleView::onTableDoubleClicked(const QModelIndex &mi)
|
||||
{
|
||||
QModelIndex sourceMi = m_proxyModel->mapToSource(mi);
|
||||
if (mi.column() == SampleModel::Column::Description ||
|
||||
mi.column() == SampleModel::Column::Pause)
|
||||
return;
|
||||
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> data = m_model->sampleData(sourceMi).legatusData;
|
||||
|
||||
DetailViewDlg *dvd = m_model->sampleData(sourceMi).dialog;
|
||||
if (dvd)
|
||||
{
|
||||
dvd->fill(*data.data());
|
||||
dvd->setWindowTitle(QString::fromLatin1("%1 (%2)").arg(m_model->sampleData(sourceMi).description,
|
||||
m_model->sampleData(sourceMi).topicName));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
dvd = new DetailViewDlg(this);
|
||||
connect(dvd, &DetailViewDlg::dataChanged, this, &SampleView::onUpdateData);
|
||||
connect(dvd, &DetailViewDlg::send, this, &SampleView::send);
|
||||
dvd->init(*data.data(), m_model->sampleData(sourceMi).topicName, RwMode::WriteOnly);
|
||||
dvd->setBtnsSendOkCancel();
|
||||
dvd->setWindowTitle(QString::fromLatin1("%1 (%2)").arg(m_model->sampleData(sourceMi).description,
|
||||
m_model->sampleData(sourceMi).topicName));
|
||||
m_model->setDialog(sourceMi, dvd);
|
||||
}
|
||||
dvd->setConnected(m_bConnected);
|
||||
dvd->show();
|
||||
dvd->adjustSize();
|
||||
}
|
||||
|
||||
void SampleView::onSendSample(const QModelIndex &mi)
|
||||
{
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> data = m_model->sampleData(m_proxyModel->mapToSource(mi)).legatusData;
|
||||
emit dataClicked(*data.data());
|
||||
}
|
||||
|
||||
void SampleView::onCustomContextMenuRequested(const QPoint &p)
|
||||
{
|
||||
const auto selIndexes = selectionModel()->selectedRows();
|
||||
|
||||
QMenu menu(this);
|
||||
|
||||
menu.addAction(tr("New"), this, &SampleView::onNewTopicRequested);
|
||||
// Only allowg, when a row is selected
|
||||
if (selIndexes.count() == 1)
|
||||
menu.addAction(tr("Delete"), this, &SampleView::onDeleteIndex);
|
||||
if (selIndexes.size() == 1)
|
||||
menu.addAction(tr("Copy"), this, &SampleView::onCopyTelegram);
|
||||
if (!m_copiedSampleData.topicName.isEmpty())
|
||||
menu.addAction(tr("Paste"), this, &SampleView::onPasteTelegram);
|
||||
if (m_model->rowCount() != 0)
|
||||
menu.addAction(tr("Clear"), m_model, &SampleModel::onClear);
|
||||
|
||||
// Only allow, when a row is selected:
|
||||
if (selIndexes.count() != 0 && m_bConnected)
|
||||
menu.addAction(tr("Send"), this, &SampleView::onSendIndex);
|
||||
|
||||
// Only allow, when a row is selected:
|
||||
if (selIndexes.size() == 1)
|
||||
menu.addAction(tr("Set current"), this, &SampleView::onSetCurrent);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
// Only allow saving all, when at least one row is available
|
||||
if (m_model->rowCount() != 0)
|
||||
menu.addAction(tr("Save all"), this, &SampleView::onSaveSampleList);
|
||||
|
||||
menu.addAction(tr("Load"), this, &SampleView::onLoadSampleList);
|
||||
|
||||
menu.exec(this->viewport()->mapToGlobal(p));
|
||||
}
|
||||
|
||||
void SampleView::onNewTopicRequested()
|
||||
{
|
||||
if (!m_topicSelector)
|
||||
{
|
||||
m_topicSelector = new TopicSelector(TopicSelector::Usage::SampleSkeletons, this);
|
||||
connect(m_topicSelector, &TopicSelector::data, this, &SampleView::onAdd);
|
||||
}
|
||||
m_topicSelector->show();
|
||||
}
|
||||
|
||||
void SampleView::onAdd(const dds::core::xtypes::DynamicData &data, const QString &topicName)
|
||||
{
|
||||
m_model->addRow(data, topicName);
|
||||
for (int i = 0; i < m_model->columnCount(QModelIndex()); ++i)
|
||||
resizeColumnToContents(i);
|
||||
}
|
||||
|
||||
void SampleView::onCopyTelegram()
|
||||
{
|
||||
const auto selIndexes = selectedIndexes();
|
||||
if (selIndexes.isEmpty())
|
||||
return;
|
||||
QModelIndex sourceMi = m_proxyModel->mapToSource(selIndexes.value(0));
|
||||
m_copiedSampleData = m_model->sampleData(sourceMi);
|
||||
}
|
||||
|
||||
void SampleView::onPasteTelegram()
|
||||
{
|
||||
if (m_copiedSampleData.topicName.isEmpty())
|
||||
return;
|
||||
m_model->addRow(m_copiedSampleData);
|
||||
}
|
||||
|
||||
void SampleView::onDeleteIndex()
|
||||
{
|
||||
QModelIndex sourceMi = m_proxyModel->mapToSource(selectedIndexes().value(0));
|
||||
m_model->deleteRow(sourceMi);
|
||||
}
|
||||
|
||||
void SampleView::onSendIndex()
|
||||
{
|
||||
const auto selIndexes = selectedIndexes();
|
||||
if (selIndexes.isEmpty())
|
||||
return;
|
||||
QModelIndex sourceMi = m_proxyModel->mapToSource(selIndexes.value(0));
|
||||
const SampleModel::SampleData sampleData = m_model->sampleData(sourceMi);
|
||||
emit send(sampleData.legatusData, sampleData.topicName);
|
||||
}
|
||||
|
||||
void SampleView::onUpdateData(const QSharedPointer<dds::core::xtypes::DynamicData> &data,
|
||||
const QString &topicName)
|
||||
{
|
||||
DetailViewDlg *dlg = qobject_cast<DetailViewDlg *>(sender());
|
||||
if (!dlg)
|
||||
return;
|
||||
|
||||
QModelIndex index = m_model->indexByDialog(dlg);
|
||||
if (index.isValid())
|
||||
m_model->updateRow(index, data, topicName);
|
||||
|
||||
}
|
||||
|
||||
void SampleView::onSetCurrent()
|
||||
{
|
||||
QModelIndex mi = selectedIndexes().value(0);
|
||||
m_proxyModel->setCurrentRow(mi.row());
|
||||
verticalHeader()->resizeSections(QHeaderView::ResizeToContents);
|
||||
}
|
||||
|
||||
void SampleView::onPlayChecked(bool down)
|
||||
{
|
||||
// Unchecked
|
||||
if (!down)
|
||||
{
|
||||
m_playTimer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// If no valid row, start from first row
|
||||
if (currentRow() < 0)
|
||||
setCurrentRow(0);
|
||||
|
||||
onPlayTimerTimeout(false);
|
||||
}
|
||||
|
||||
void SampleView::onPlayTimerTimeout(bool moveOn)
|
||||
{
|
||||
// Move to next row
|
||||
if (moveOn)
|
||||
{
|
||||
setCurrentRow(currentRow() + 1);
|
||||
// If moved beyond end
|
||||
if (currentRow() < 0)
|
||||
{
|
||||
// start over
|
||||
if (m_repeat)
|
||||
setCurrentRow(0);
|
||||
else
|
||||
{
|
||||
// or stop
|
||||
emit playerActive(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SampleModel::SampleData sampleData = m_model->sampleData(m_proxyModel->mapToSource(m_proxyModel->index(currentRow(), 0)));
|
||||
if (sampleData.legatusData)
|
||||
emit send(sampleData.legatusData, sampleData.topicName);
|
||||
|
||||
int pause = m_model->sampleData(m_proxyModel->index(currentRow(), 0)).pause;
|
||||
|
||||
if (pause > 0)
|
||||
m_playTimer.start(pause);
|
||||
else
|
||||
emit playerActive(false);
|
||||
}
|
||||
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableView for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QTableView>
|
||||
#include <QTimer>
|
||||
#include <dds/dds.hpp>
|
||||
#include "logger.h"
|
||||
#include "samplemodel.h"
|
||||
|
||||
class SampleSortModel;
|
||||
class DetailViewDlg;
|
||||
class TopicSelector;
|
||||
|
||||
class SampleView : public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SampleView(QWidget *parent = nullptr);
|
||||
~SampleView();
|
||||
|
||||
SampleModel *sampleModel() {return m_model;}
|
||||
|
||||
void setConnected(bool bConnected) { m_bConnected = bConnected; }
|
||||
|
||||
int currentRow() const;
|
||||
void setCurrentRow(int row);
|
||||
|
||||
public slots:
|
||||
|
||||
void onPlayChecked(bool down);
|
||||
void onSetRepeat(bool repeat) { m_repeat = repeat; }
|
||||
|
||||
void onSaveSampleList();
|
||||
void onLoadSampleList();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void init();
|
||||
|
||||
SampleSortModel *m_proxyModel;
|
||||
SampleModel *m_model;
|
||||
TopicSelector *m_topicSelector;
|
||||
QTimer m_playTimer;
|
||||
bool m_repeat;
|
||||
bool m_bConnected;
|
||||
SampleModel::SampleData m_copiedSampleData;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
private slots:
|
||||
void onSendSample(const QModelIndex &mi);
|
||||
void onTableDoubleClicked(const QModelIndex &mi);
|
||||
void onCustomContextMenuRequested(const QPoint &p);
|
||||
void onNewTopicRequested();
|
||||
void onAdd(const dds::core::xtypes::DynamicData &data, const QString &topicName);
|
||||
void onCopyTelegram();
|
||||
void onPasteTelegram();
|
||||
void onDeleteIndex();
|
||||
void onSendIndex();
|
||||
void onUpdateData(const QSharedPointer<dds::core::xtypes::DynamicData> &data,
|
||||
const QString &topicName);
|
||||
void onSetCurrent();
|
||||
void onPlayTimerTimeout(bool moveOn = true);
|
||||
|
||||
signals:
|
||||
void dataClicked(const dds::core::xtypes::DynamicData &data);
|
||||
void send(const QSharedPointer<dds::core::xtypes::DynamicData> &data,
|
||||
const QString &topicName);
|
||||
void playerActive(bool active);
|
||||
|
||||
};
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* ABC widget for creating custom views of dds sequences
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Jens Heilmann, SLTE, 2021-05-17
|
||||
*/
|
||||
#include "abstractsequencewidget.h"
|
||||
|
||||
#include <QToolButton>
|
||||
|
||||
AbstractSequenceWidget::AbstractSequenceWidget(const RwMode &readOnly,
|
||||
const QString &name,
|
||||
const dds::core::xtypes::DynamicType &contentType,
|
||||
NGL::INGLLogger &logger,
|
||||
QWidget *parent)
|
||||
: QGroupBox(name,parent)
|
||||
, m_rwMode(readOnly)
|
||||
, m_pbAddMember(new QToolButton)
|
||||
, m_contentType(contentType)
|
||||
, m_name(name)
|
||||
, m_logger(logger)
|
||||
{
|
||||
m_pbAddMember->setIcon(QIcon(QLatin1String(":/icons/png/add.png")));
|
||||
|
||||
connect(m_pbAddMember, &QToolButton::clicked, [&] () {
|
||||
const uint32_t &index = m_memberList.size() + 1;
|
||||
QString findObjectName = getObjectName(index);
|
||||
|
||||
if(findChild<QWidget *>(findObjectName))
|
||||
return;
|
||||
|
||||
QWidget *child = DetailView::createWidget(m_rwMode, m_contentType, findObjectName, m_logger);
|
||||
addMemberWidget(child);
|
||||
});
|
||||
|
||||
if(m_rwMode == RwMode::ReadOnly)
|
||||
m_pbAddMember->setDisabled(true);
|
||||
}
|
||||
|
||||
AbstractSequenceWidget::~AbstractSequenceWidget()
|
||||
{
|
||||
delete m_pbAddMember;
|
||||
}
|
||||
|
||||
void AbstractSequenceWidget::setValue(const dds::core::xtypes::DynamicData &inner,
|
||||
const uint32_t &index, const QWidget *useOn)
|
||||
{
|
||||
QString findObjectName = getObjectName(index);
|
||||
QWidget *child = findChild<QWidget *>(findObjectName);
|
||||
|
||||
if(!child)
|
||||
{
|
||||
child = DetailView::createWidget(m_rwMode,
|
||||
m_contentType,
|
||||
findObjectName,
|
||||
m_logger);
|
||||
|
||||
addMemberWidget(child);
|
||||
}
|
||||
|
||||
DetailView::setWidgetData(inner,
|
||||
m_contentType.kind(),
|
||||
index,
|
||||
findObjectName,
|
||||
useOn,
|
||||
m_logger);
|
||||
|
||||
}
|
||||
|
||||
void AbstractSequenceWidget::getValue(const QString &name, dds::core::xtypes::DynamicData &data,
|
||||
const uint32_t &index, const QWidget *useOn) const
|
||||
{
|
||||
QString findObjectName = name + QLatin1Char('.') + QString::number(index);
|
||||
|
||||
if(!findChild<QWidget *>(findObjectName))
|
||||
return;
|
||||
|
||||
DetailView::getWidgetData(data,
|
||||
m_contentType.kind(),
|
||||
index,
|
||||
findObjectName,
|
||||
useOn,
|
||||
m_logger);
|
||||
}
|
||||
|
||||
void AbstractSequenceWidget::setMemberCount(int count)
|
||||
{
|
||||
QWidget *child = nullptr;
|
||||
do
|
||||
{
|
||||
child = findChild<QWidget *>(getObjectName(++count));
|
||||
delete child;
|
||||
}
|
||||
while (child);
|
||||
m_memberList.removeAll(nullptr);
|
||||
}
|
||||
|
||||
uint32_t AbstractSequenceWidget::getMemberCount() const
|
||||
{
|
||||
int count = 0;
|
||||
QWidget *child = findChild<QWidget *>(getObjectName(count+1));
|
||||
while(child)
|
||||
{
|
||||
count++;
|
||||
child = findChild<QWidget *>(getObjectName(count+1));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
NGL::INGLLogger &AbstractSequenceWidget::logger() const
|
||||
{
|
||||
return m_logger;
|
||||
}
|
||||
|
||||
QToolButton *AbstractSequenceWidget::pbAddMember() const
|
||||
{
|
||||
return m_pbAddMember;
|
||||
}
|
||||
|
||||
QString AbstractSequenceWidget::getObjectName(int index) const
|
||||
{
|
||||
return m_name + QLatin1Char('.') + QString::number(index);
|
||||
}
|
||||
|
||||
void AbstractSequenceWidget::addMemberWidget(QWidget *member)
|
||||
{
|
||||
QToolButton *button = new QToolButton(member);
|
||||
button->setIcon(QIcon(QLatin1String(":/icons/png/remove.png")));
|
||||
|
||||
connect(button, &QToolButton::clicked, member, &QWidget::deleteLater);
|
||||
connect(member, &QWidget::destroyed, this, &AbstractSequenceWidget::deleteMemberWidget);
|
||||
connect(member, &QWidget::destroyed, button, &QToolButton::deleteLater);
|
||||
|
||||
if(m_rwMode == RwMode::ReadOnly)
|
||||
button->setDisabled(true);
|
||||
|
||||
DEBUG_LOG(m_logger) << "new member widget: " << member->objectName();
|
||||
m_memberList.push_back(member);
|
||||
setupMemberWidget(member, button, member->objectName());
|
||||
}
|
||||
|
||||
void AbstractSequenceWidget::deleteMemberWidget(QObject *member)
|
||||
{
|
||||
QWidget *w = qobject_cast<QWidget*>(member);
|
||||
|
||||
if(!w)
|
||||
return;
|
||||
|
||||
for(auto child : w->children()) // necessary?
|
||||
child->deleteLater();
|
||||
|
||||
m_memberList.removeAll(w);
|
||||
for(int i=0; i<m_memberList.size(); i++)
|
||||
{
|
||||
m_memberList[i]->setObjectName(getObjectName(i + 1));
|
||||
|
||||
DEBUG_LOG(m_logger) << "new name: " << m_memberList[i]->objectName();
|
||||
for(auto child : m_memberList[i]->children())
|
||||
{
|
||||
if(child->objectName().isEmpty())
|
||||
continue;
|
||||
|
||||
QString childName = child->objectName();
|
||||
QString newId = m_name + QLatin1Char('.') + QString::number(i + 1);
|
||||
child->setObjectName(childName.replace(QRegExp(m_name + QLatin1String(".([0-9]*)")), newId));
|
||||
DEBUG_LOG(m_logger) << "new child name: " << child->objectName();
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* ABC for creating custom views of dds sequences
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Jens Heilmann, SLTE, 2021-05-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <dds/dds.hpp>
|
||||
#endif
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QList>
|
||||
|
||||
#include <detailview.h>
|
||||
#include "logger.h"
|
||||
|
||||
class QToolButton;
|
||||
|
||||
class AbstractSequenceWidget : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief AbstractSequenceWidget inits pbAddMember and adds logic to it.
|
||||
* @attention pbAddMember must be inserted into a layout
|
||||
* @param readOnly if read only, disable all buttons on creation
|
||||
* @param name
|
||||
* @param contentType
|
||||
* @param logger
|
||||
* @param parent
|
||||
*/
|
||||
explicit AbstractSequenceWidget(const RwMode &readOnly,
|
||||
const QString &name,
|
||||
const dds::core::xtypes::DynamicType &contentType,
|
||||
NGL::INGLLogger &logger,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
~AbstractSequenceWidget();
|
||||
|
||||
/**
|
||||
* @brief setValue set data from dds for widget at index
|
||||
* if widget not found, createa a new one
|
||||
* @param inner
|
||||
* @param index
|
||||
* @param useOn
|
||||
*/
|
||||
virtual void setValue(const dds::core::xtypes::DynamicData &inner,
|
||||
const uint32_t &index, const QWidget *useOn);
|
||||
|
||||
/**
|
||||
* @brief getValue get data inside widget for dds
|
||||
* @param name
|
||||
* @param data
|
||||
* @param index
|
||||
* @param useOn
|
||||
*/
|
||||
virtual void getValue(const QString &name, dds::core::xtypes::DynamicData &data,
|
||||
const uint32_t &index, const QWidget *useOn) const;
|
||||
|
||||
/**
|
||||
* @brief setMemberCount used by DetailView
|
||||
* @param count
|
||||
*/
|
||||
virtual void setMemberCount(int count);
|
||||
|
||||
/**
|
||||
* @brief getMemberCount used by DetailView
|
||||
* @return
|
||||
*/
|
||||
virtual uint32_t getMemberCount() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief logger
|
||||
* @return
|
||||
*/
|
||||
virtual NGL::INGLLogger &logger() const;
|
||||
|
||||
/**
|
||||
* @brief pbAddMember
|
||||
* @return
|
||||
*/
|
||||
virtual QToolButton *pbAddMember() const;
|
||||
|
||||
/**
|
||||
* @brief getObjectName generates a valid object name given by index
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
virtual QString getObjectName(int index) const;
|
||||
|
||||
/**
|
||||
* @brief setupMemberWidget place child and button widget into a layout
|
||||
* @param child
|
||||
* @param pbChild
|
||||
* @param objectName
|
||||
*/
|
||||
virtual void setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName) = 0;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief addMemberWidget create a new button and connect to child
|
||||
* @param member new child
|
||||
*/
|
||||
virtual void addMemberWidget(QWidget *member);
|
||||
|
||||
/**
|
||||
* @brief deleteMemberWidget clean up child and reindex other children objectnames
|
||||
* @param member deleted child
|
||||
*/
|
||||
virtual void deleteMemberWidget(QObject *member);
|
||||
|
||||
private:
|
||||
RwMode m_rwMode;
|
||||
QToolButton *m_pbAddMember = nullptr;
|
||||
QList<QWidget*> m_memberList;
|
||||
dds::core::xtypes::DynamicType m_contentType;
|
||||
QString m_name;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* widget for creating a list view of dds sequences
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Jens Heilmann, SLTE, 2021-05-17
|
||||
*/
|
||||
#include "listsequencewidget.h"
|
||||
#include <QToolButton>
|
||||
|
||||
ListSequenceWidget::ListSequenceWidget(const RwMode &readOnly,
|
||||
const QString &name,
|
||||
const dds::core::xtypes::DynamicType &contentType,
|
||||
NGL::INGLLogger &logger,
|
||||
QWidget *parent)
|
||||
: AbstractSequenceWidget(readOnly, name,contentType, logger, parent)
|
||||
{
|
||||
QGridLayout *gridLayout = new QGridLayout(this);
|
||||
gridLayout->addWidget(pbAddMember(),0,2);
|
||||
}
|
||||
|
||||
ListSequenceWidget::~ListSequenceWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void ListSequenceWidget::setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName)
|
||||
{
|
||||
QLabel *label = new QLabel(child);
|
||||
label->setText(objectName);
|
||||
|
||||
connect(child, &QWidget::objectNameChanged, label, &QLabel::setText);
|
||||
connect(child, &QWidget::destroyed, label, &QLabel::deleteLater);
|
||||
|
||||
QGridLayout *gridLayout = qobject_cast<QGridLayout*>(layout());
|
||||
Q_ASSERT(gridLayout);
|
||||
|
||||
int newRow = gridLayout->rowCount() +1;
|
||||
gridLayout->addWidget(label, newRow, 0);
|
||||
gridLayout->addWidget(child, newRow, 1);
|
||||
gridLayout->addWidget(pbChild, newRow, 2);
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* widget for creating a list view of dds sequences
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Jens Heilmann, SLTE, 2021-05-17
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "abstractsequencewidget.h"
|
||||
|
||||
class ListSequenceWidget : public AbstractSequenceWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListSequenceWidget(const RwMode &readOnly,
|
||||
const QString &name,
|
||||
const dds::core::xtypes::DynamicType &contentType,
|
||||
NGL::INGLLogger &logger,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
~ListSequenceWidget();
|
||||
|
||||
protected:
|
||||
void setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName) override;
|
||||
};
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* widget for creating a tab view of dds sequences
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Jens Heilmann, SLTE, 2021-05-17
|
||||
*/
|
||||
#include "tabsequencewidget.h"
|
||||
|
||||
#include <QTabBar>
|
||||
#include <QTabWidget>
|
||||
#include <QToolButton>
|
||||
|
||||
TabSequenceWidget::TabSequenceWidget(const RwMode &readOnly,
|
||||
const QString &name,
|
||||
const dds::core::xtypes::DynamicType &contentType,
|
||||
NGL::INGLLogger &logger,
|
||||
QWidget *parent)
|
||||
: AbstractSequenceWidget(readOnly, name,contentType, logger, parent)
|
||||
, m_twSequences(new QTabWidget(this))
|
||||
{
|
||||
QVBoxLayout *vboxLayout = new QVBoxLayout(this);
|
||||
vboxLayout->addWidget(m_twSequences);
|
||||
|
||||
m_twSequences->setCornerWidget(pbAddMember());
|
||||
pbAddMember()->setFixedSize(m_twSequences->tabBar()->size());
|
||||
pbAddMember()->setIconSize(m_twSequences->tabBar()->size());
|
||||
}
|
||||
|
||||
TabSequenceWidget::~TabSequenceWidget()
|
||||
{
|
||||
delete m_twSequences;
|
||||
}
|
||||
|
||||
void TabSequenceWidget::setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName)
|
||||
{
|
||||
int index = m_twSequences->addTab(child, objectName);
|
||||
m_twSequences->tabBar()->setTabButton(index, QTabBar::RightSide, pbChild);
|
||||
|
||||
connect(child, &QWidget::objectNameChanged, this, [child, this] (QString text) {
|
||||
m_twSequences->setTabText(m_twSequences->indexOf(child), text);
|
||||
});
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* widget for creating a tab view of dds sequences
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Jens Heilmann, SLTE, 2021-05-17
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "abstractsequencewidget.h"
|
||||
|
||||
class QTabWidget;
|
||||
|
||||
class TabSequenceWidget : public AbstractSequenceWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TabSequenceWidget(const RwMode &readOnly,
|
||||
const QString &name,
|
||||
const dds::core::xtypes::DynamicType &contentType,
|
||||
NGL::INGLLogger &logger,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
virtual ~TabSequenceWidget();
|
||||
|
||||
protected:
|
||||
void setupMemberWidget(QWidget *child, QToolButton *pbChild, const QString &objectName) override;
|
||||
|
||||
private:
|
||||
QTabWidget *m_twSequences = nullptr;
|
||||
|
||||
};
|
||||
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Subscriber (DataSampleReceiver)
|
||||
* Uses DDTCs to subscribe to data
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#include "subscriber.h"
|
||||
#include "telegrammodel.h"
|
||||
|
||||
namespace DdsView
|
||||
{
|
||||
|
||||
Subscriber::Subscriber(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const std::string &partition,
|
||||
TelegramModel *tm)
|
||||
: DDSConnector::Subscriber(domainParticipant, partition)
|
||||
, m_telegramModel(tm)
|
||||
, m_logger(DdsView::getLogger("Subscriber"))
|
||||
{}
|
||||
|
||||
Subscriber::~Subscriber()
|
||||
{
|
||||
qDeleteAll(m_ddtcMap);
|
||||
m_ddtcMap.clear();
|
||||
}
|
||||
|
||||
void Subscriber::processData(const dds::core::xtypes::DynamicData &data, const QString &topicName, DataModification modType)
|
||||
{
|
||||
m_telegramModel->processData(data, topicName, modType);
|
||||
}
|
||||
|
||||
bool Subscriber::initDDTCs(const QVector<DDSGeneric::TopicDescription> &vDro)
|
||||
{
|
||||
// TODO: remove old droEntries
|
||||
|
||||
QMap<std::string, ddtc *> newTopics;
|
||||
|
||||
for (const DDSGeneric::TopicDescription &ti : vDro)
|
||||
{
|
||||
auto it = m_ddtcMap.find(ti.name);
|
||||
if (it != m_ddtcMap.end())
|
||||
{
|
||||
// already there
|
||||
newTopics.insert(it.key(), it.value());
|
||||
m_ddtcMap.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
DTC::Ddtc<DDSGeneric::DynamicDataReader> *ddtc = this->create(
|
||||
[this](const dds::core::xtypes::DynamicData & data, const std::string & topicName, DataModification modType)
|
||||
{ processData(data, QString::fromStdString(topicName), modType); },
|
||||
ti.type,
|
||||
nullptr /* Use SubscriberListener from DdsConnector */,
|
||||
ti.name
|
||||
);
|
||||
if (ddtc == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Can't create ddtc for Topic (" << ti.name << "), TypeName (" << ti.typeName << ")";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ddtc->hasSamples())
|
||||
{
|
||||
for (const auto &data : ddtc->readAlive())
|
||||
processData(data.data(), QString::fromStdString(ti.name), DataModification::eAdded);
|
||||
}
|
||||
newTopics.insert(ti.name, ddtc);
|
||||
}
|
||||
qDeleteAll(m_ddtcMap);
|
||||
m_ddtcMap = newTopics;
|
||||
|
||||
return true;
|
||||
} // end initDDTCs()
|
||||
|
||||
} // Namespace DdsView
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Subscriber (DataSampleReceiver)
|
||||
* Uses DDTCs to subscribe to data
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <DDS/DDSConnector/subscriber.h>
|
||||
#include <DDS/Generic/topic_type_plugin.h>
|
||||
#include "logger.h"
|
||||
|
||||
class TelegramModel;
|
||||
|
||||
namespace DdsView
|
||||
{
|
||||
class TopicData;
|
||||
|
||||
class Subscriber : public DDSConnector::Subscriber
|
||||
{
|
||||
|
||||
public:
|
||||
Subscriber(const dds::domain::DomainParticipant &domainParticipant,
|
||||
const std::string &partition,
|
||||
TelegramModel *tm);
|
||||
~Subscriber();
|
||||
|
||||
// Process data dynamically
|
||||
void processData(const dds::core::xtypes::DynamicData &data, const QString &topicName, DataModification modType);
|
||||
|
||||
bool initDDTCs(const QVector<DDSGeneric::TopicDescription> &vDro);
|
||||
|
||||
private:
|
||||
using ddtc = DTC::Ddtc<DDSGeneric::DynamicDataReader>;
|
||||
QMap<std::string, ddtc *> m_ddtcMap;
|
||||
TelegramModel *m_telegramModel;
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
}
|
||||
Executable
+464
@@ -0,0 +1,464 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableModel for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "telegrammodel.h"
|
||||
#include "nglsettings.h"
|
||||
#include "samplemodel.h"
|
||||
#include "xmlhelper.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QMimeData>
|
||||
#include <QIODevice>
|
||||
#include <QIcon>
|
||||
|
||||
TelegramModel::TelegramModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_data()
|
||||
, m_infoList(NGLSettings::instance()->infoValues())
|
||||
, m_logger(DdsView::getLogger("TelegramModel"))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int TelegramModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
int TelegramModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return Column::Maximum + 1;
|
||||
}
|
||||
|
||||
QVariant TelegramModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
if (role != Qt::DisplayRole &&
|
||||
role != Qt::DecorationRole)
|
||||
return QVariant();
|
||||
if (index.column() >= columnCount(QModelIndex()) ||
|
||||
index.row() >= rowCount(QModelIndex()))
|
||||
return QVariant();
|
||||
|
||||
const TelegramData &data = m_data[index.row()];
|
||||
if (data.typeName.empty())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DecorationRole)
|
||||
{
|
||||
if (index.column() != 0)
|
||||
return QVariant();
|
||||
switch (data.modType)
|
||||
{
|
||||
case DataModification::eAdded:
|
||||
return QIcon(QLatin1String(":icons/16x16/list-add.png"));
|
||||
case DataModification::eRemoved:
|
||||
return QIcon(QLatin1String(":icons/16x16/list-remove.png"));
|
||||
case DataModification::eModified:
|
||||
return QIcon(QLatin1String(":icons/16x16/edit-redo.png"));
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case Column::DateTime:
|
||||
return data.dateTime.toString(QStringLiteral("dd.MM.yy - hh:mm:ss.zzz"));
|
||||
case Column::TopicName:
|
||||
return data.topicName;
|
||||
case Column::TypeName:
|
||||
return QString::fromStdString(data.typeName);
|
||||
case Column::Info:
|
||||
return data.info;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant TelegramModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation != Qt::Horizontal ||
|
||||
role != Qt::DisplayRole)
|
||||
return QAbstractTableModel::headerData(section, orientation, role);
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case Column::DateTime:
|
||||
return tr("Timestamp");
|
||||
case Column::TopicName:
|
||||
return tr("Topic Name");
|
||||
case Column::TypeName:
|
||||
return tr("Type Name");
|
||||
case Column::Info:
|
||||
return tr("Info");
|
||||
}
|
||||
return QAbstractTableModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
const QString TelegramModel::topicName(const QModelIndex &index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_data.size())
|
||||
return QString();
|
||||
return m_data[row].topicName;
|
||||
}
|
||||
|
||||
const std::string TelegramModel::typeName(const QModelIndex &index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_data.size())
|
||||
return std::string();
|
||||
return m_data[row].typeName;
|
||||
}
|
||||
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> TelegramModel::dynamicData(const QModelIndex &index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_data.size())
|
||||
return QSharedPointer<dds::core::xtypes::DynamicData>();
|
||||
return m_data[row].legatusData;
|
||||
}
|
||||
|
||||
Qt::ItemFlags TelegramModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
return (QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
|
||||
}
|
||||
|
||||
bool TelegramModel::deleteRow(const QModelIndex &index)
|
||||
{
|
||||
int position = index.row();
|
||||
if (position < 0 || position > m_data.size())
|
||||
return false;
|
||||
|
||||
beginRemoveRows(QModelIndex(), position, position);
|
||||
m_data.remove(position);
|
||||
endRemoveRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TelegramModel::onClear()
|
||||
{
|
||||
beginResetModel();
|
||||
m_data.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
||||
void TelegramModel::processData(const dds::core::xtypes::DynamicData &data, const QString &topicName, DataModification modType)
|
||||
{
|
||||
TelegramData telegram;
|
||||
telegram.dateTime = QDateTime::currentDateTime();
|
||||
telegram.typeName = data.type().name();
|
||||
telegram.topicName = topicName;
|
||||
telegram.modType = modType;
|
||||
telegram.legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(new dds::core::xtypes::DynamicData(data));
|
||||
telegram.info = getInfo(*telegram.legatusData.data(), telegram.topicName);
|
||||
|
||||
beginInsertRows(QModelIndex(), m_data.size(), m_data.size());
|
||||
m_data.push_back(std::move(telegram));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
TelegramModel::TelegramData TelegramModel::telegramData(const QModelIndex &index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_data.size())
|
||||
return TelegramData();
|
||||
return m_data[row];
|
||||
}
|
||||
|
||||
QMimeData *TelegramModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
|
||||
QByteArray encodedData;
|
||||
QDataStream stream(&encodedData, QIODevice::WriteOnly);
|
||||
|
||||
for (const QModelIndex &mi : indexes)
|
||||
{
|
||||
if (!mi.isValid())
|
||||
continue;
|
||||
|
||||
if (mi.column() != 0)
|
||||
continue;
|
||||
|
||||
const TelegramData &telegram = m_data[mi.row()];
|
||||
|
||||
stream << quint64(telegram.legatusData.data())
|
||||
<< telegram.topicName;
|
||||
}
|
||||
mimeData->setData(QLatin1String("dds::core::xtypes::DynamicData/Pointer+TopicName"), encodedData);
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
Qt::DropActions TelegramModel::supportedDropActions() const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
}
|
||||
|
||||
bool TelegramModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
|
||||
int row, int column, const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(row)
|
||||
Q_UNUSED(column)
|
||||
Q_UNUSED(parent)
|
||||
if (action == Qt::IgnoreAction)
|
||||
return true;
|
||||
|
||||
if ((action != Qt::MoveAction) && (action != Qt::CopyAction))
|
||||
return false;
|
||||
|
||||
// Only single Selection, therefore, no while()
|
||||
if (data->hasFormat(QLatin1String("SampleModel/SampleData")))
|
||||
{
|
||||
const QByteArray encodedData = data->data(QLatin1String("SampleModel/SampleData"));
|
||||
const SampleModel::SampleData sample = SampleModel::SampleData::decodeFromMime(encodedData);
|
||||
|
||||
emit send(sample.legatusData, sample.topicName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList TelegramModel::mimeTypes() const
|
||||
{
|
||||
QStringList sl;
|
||||
sl << QLatin1String("SampleModel/SampleData"); // (SampleModel::SampleData)
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
void TelegramModel::onTypeCodeInfosChanged()
|
||||
{
|
||||
m_infoList = NGLSettings::instance()->infoValues();
|
||||
for (TelegramData &telegram : m_data)
|
||||
telegram.info = getInfo(*telegram.legatusData.data(), telegram.topicName);
|
||||
}
|
||||
|
||||
//
|
||||
// TelegramSortModel
|
||||
//
|
||||
bool TelegramSortModel::lessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const
|
||||
{
|
||||
if (left.column() != right.column())
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
TelegramModel *srcModel = qobject_cast<TelegramModel *>(sourceModel());
|
||||
if (!srcModel)
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
TelegramModel::TelegramData dataLeft = srcModel->telegramData(left);
|
||||
TelegramModel::TelegramData dataRight = srcModel->telegramData(right);
|
||||
switch (left.column())
|
||||
{
|
||||
case TelegramModel::Column::DateTime:
|
||||
return dataLeft.dateTime < dataRight.dateTime;
|
||||
case TelegramModel::Column::TopicName:
|
||||
return dataLeft.topicName < dataRight.topicName;
|
||||
case TelegramModel::Column::TypeName:
|
||||
return dataLeft.typeName < dataRight.typeName;
|
||||
case TelegramModel::Column::Info:
|
||||
return dataLeft.info < dataRight.info;
|
||||
}
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
|
||||
QString TelegramModel::getInfo(const dds::core::xtypes::DynamicData &data, const QString &topicName) const
|
||||
{
|
||||
const QStringList fields = m_infoList.value(topicName);
|
||||
const QMap<QString, QString> result = getInfoInternal(data, fields);
|
||||
QStringList sl;
|
||||
|
||||
for (const QString &str : fields)
|
||||
{
|
||||
if (result.contains(str))
|
||||
sl << QString(str + QLatin1String(": ") + result[str]);
|
||||
}
|
||||
return sl.isEmpty() ? QStringLiteral(" ") : sl.join(QLatin1String(" | "));
|
||||
}
|
||||
|
||||
QString TelegramModel::getString(const dds::core::xtypes::DynamicData &data, uint32_t member_index) const
|
||||
{
|
||||
if (!data.member_exists(member_index))
|
||||
return QString();
|
||||
|
||||
const dds::core::xtypes::DynamicType &type = data.type();
|
||||
const rti::core::xtypes::DynamicDataMemberInfo info = data.member_info(member_index);
|
||||
const dds::core::xtypes::TypeKind typeKind = info.member_kind();
|
||||
|
||||
switch (typeKind.underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
return QString::number(data.value<char>(member_index));
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
case dds::core::xtypes::TypeKind::CHAR_32_TYPE:
|
||||
#else
|
||||
case dds::core::xtypes::TypeKind::CHAR_16_TYPE:
|
||||
#endif
|
||||
return QString::number(data.value<DDS_Wchar>(member_index));
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
return QString::number(data.value<uint8_t>(member_index));
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
return QString::number(data.value<int16_t>(member_index));
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
return QString::number(data.value<uint16_t>(member_index));
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
return QString::number(data.value<int32_t>(member_index));
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
return QString::number(data.value<uint32_t>(member_index));
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
return QString::number(data.value<int64_t>(member_index));
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
return QString::number(data.value<uint64_t>(member_index));
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
return QString::number(data.value<float>(member_index));
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
return QString::number(data.value<double>(member_index));
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
return data.value<bool>(member_index) ? QLatin1String("Yes") : QLatin1String("No");
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
{
|
||||
dds::core::xtypes::StructType structTypeParent = static_cast<const dds::core::xtypes::StructType &>(type);
|
||||
const int ordinal = data.value<int32_t>(member_index);
|
||||
|
||||
unsigned int rowInType = structTypeParent.find_member_by_name(info.member_name());
|
||||
while (rowInType == dds::core::xtypes::EnumType::INVALID_INDEX)
|
||||
{
|
||||
if (!structTypeParent.has_parent())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Could not retrieve enum value from Struct Type";
|
||||
return QStringLiteral("<%1>").arg(ordinal);
|
||||
}
|
||||
structTypeParent = structTypeParent.parent();
|
||||
rowInType = structTypeParent.find_member_by_name(info.member_name());
|
||||
}
|
||||
const dds::core::xtypes::Member &enumMember = structTypeParent.member(info.member_name());
|
||||
const dds::core::xtypes::EnumType &inner = static_cast<const dds::core::xtypes::EnumType &>(enumMember.type());
|
||||
|
||||
QString v = QStringLiteral("%1 (%2)").arg(QString::fromStdString(inner.name())).arg(ordinal);
|
||||
for (size_t i = 0; i < inner.member_count(); ++i)
|
||||
{
|
||||
const auto &member = inner.member(i);
|
||||
if (member.ordinal() == ordinal)
|
||||
{
|
||||
v = QStringLiteral("%1 (%2)").arg(QString::fromStdString(member.name())).arg(ordinal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
const dds::core::string tmp = data.value<dds::core::string>(member_index);
|
||||
if (tmp.c_str()) // unset -> nullptr
|
||||
return QString::fromUtf8(tmp.c_str());
|
||||
return tr("(empty)");
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::DynamicData inner = data.value<dds::core::xtypes::DynamicData>(member_index);
|
||||
const dds::core::xtypes::SequenceType &seqType = static_cast<const dds::core::xtypes::SequenceType &>(inner.type());
|
||||
|
||||
|
||||
if (!dds::core::xtypes::is_constructed_type(seqType.content_type())) // Primitive Sequence
|
||||
{
|
||||
QStringList data;
|
||||
data.reserve(inner.member_count());
|
||||
for (uint32_t inner_index = 1; inner_index <= inner.member_count(); ++inner_index)
|
||||
data << getString(inner, inner_index);
|
||||
|
||||
return data.join(QLatin1String(", "));
|
||||
}
|
||||
// TODO: impl Struct Sequence
|
||||
WARNING_LOG(m_logger) << "Struct Sequence not implemented yet (because €€€)! returning info string";
|
||||
return QLatin1String("[Struct Sequence - impl. missing]");
|
||||
}
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
QMap<QString, QString> TelegramModel::getInfoInternal(const dds::core::xtypes::DynamicData &data, const QStringList &fields) const
|
||||
{
|
||||
QMap<QString, QString> infotext;
|
||||
for (const QString &field : fields)
|
||||
{
|
||||
QString realField(field);
|
||||
dds::core::xtypes::DynamicData realData = data;
|
||||
int idx;
|
||||
while ((idx = realField.indexOf(QLatin1Char('.'))) != -1)
|
||||
{
|
||||
// when it's optional and not available it will throw
|
||||
try {
|
||||
realData = data.value<dds::core::xtypes::DynamicData>(realField.left(idx).toStdString());
|
||||
realField = realField.mid(idx + 1);
|
||||
} catch (...)
|
||||
{
|
||||
break; // optional, non-existing value
|
||||
}
|
||||
}
|
||||
const std::string str = realField.toStdString();
|
||||
if (realData.member_exists(str))
|
||||
infotext.insert(field, getString(realData, realData.member_index(str)));
|
||||
}
|
||||
return infotext;
|
||||
}
|
||||
|
||||
void TelegramModel::TelegramData::toXml(QDomDocument &doc, QDomElement &de) const
|
||||
{
|
||||
QDomElement deSample = doc.createElement(QLatin1String("Sample"));
|
||||
de.appendChild(deSample);
|
||||
|
||||
QDomElement deDateTime = doc.createElement(QLatin1String("DateTime"));
|
||||
deSample.appendChild(deDateTime);
|
||||
QDomText deDateTimeContents = doc.createTextNode(dateTime.toString(Qt::ISODate));
|
||||
deDateTime.appendChild(deDateTimeContents);
|
||||
|
||||
XmlHelper::dds2Xml(*legatusData.data(), topicName, doc, deSample);
|
||||
// XmlHelper::dds2Xml(*legatusData.data(), doc, deSample);
|
||||
}
|
||||
|
||||
bool TelegramModel::TelegramData::fromXml(QDomElement &de)
|
||||
{
|
||||
if (de.nodeName().compare(QLatin1String("Sample"), Qt::CaseInsensitive) != 0)
|
||||
return false;
|
||||
|
||||
bool succ = false;
|
||||
QDomNodeList dnl = de.childNodes();
|
||||
for (int i = 0; i < dnl.size(); ++i)
|
||||
{
|
||||
QDomNode dn = dnl.at(i);
|
||||
if (!dn.isElement())
|
||||
continue;
|
||||
QDomElement innerDe = dn.toElement();
|
||||
if (innerDe.nodeName().compare(QLatin1String("DateTime"), Qt::CaseInsensitive) != 0)
|
||||
dateTime = QDateTime::fromString(innerDe.text(), Qt::ISODate);
|
||||
else if (innerDe.nodeName().compare(QLatin1String("DdsData"), Qt::CaseInsensitive) != 0)
|
||||
{
|
||||
const QString topicName = XmlHelper::topicNameFromXml(de);
|
||||
const TopicDescriptionEx td = NGLSettings::instance()->topicFromTopicName(topicName.toStdString());
|
||||
const dds::core::xtypes::DynamicData data = XmlHelper::xml2Dds(innerDe, td);
|
||||
if (data.type_kind() == dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
{
|
||||
const dds::core::xtypes::StructType &type = static_cast<const dds::core::xtypes::StructType &>(data.type());
|
||||
legatusData = QSharedPointer<dds::core::xtypes::DynamicData>(new dds::core::xtypes::DynamicData(data));
|
||||
typeName = type.name();
|
||||
succ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return succ;
|
||||
}
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableModel for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QVector>
|
||||
#include <QDateTime>
|
||||
#include <QSharedPointer>
|
||||
#include <QStringList>
|
||||
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
#include "logger.h"
|
||||
|
||||
class QDomDocument;
|
||||
class QDomElement;
|
||||
class TelegramSortModel;
|
||||
|
||||
class TelegramModel final : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TelegramModel(QObject *parent = nullptr);
|
||||
|
||||
enum Column
|
||||
{
|
||||
DateTime = 0,
|
||||
TopicName = 1,
|
||||
TypeName = 2,
|
||||
Info = 3,
|
||||
Maximum = Info
|
||||
};
|
||||
|
||||
struct TelegramData
|
||||
{
|
||||
QDateTime dateTime;
|
||||
std::string typeName;
|
||||
QString topicName;
|
||||
QString info;
|
||||
DataModification modType;
|
||||
QSharedPointer<dds::core::xtypes::DynamicData> legatusData;
|
||||
|
||||
void toXml(QDomDocument &doc, QDomElement &de) const;
|
||||
bool fromXml(QDomElement &de);
|
||||
};
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
TelegramData telegramData(const QModelIndex &index) const;
|
||||
|
||||
void processData(const dds::core::xtypes::DynamicData &data, const QString &topicName, DataModification modType);
|
||||
|
||||
const QString topicName(const QModelIndex &index) const;
|
||||
const std::string typeName(const QModelIndex &index) const;
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> dynamicData(const QModelIndex &index) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
Qt::DropActions supportedDropActions() const override;
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
QStringList mimeTypes() const override;
|
||||
|
||||
void onTypeCodeInfosChanged();
|
||||
|
||||
bool deleteRow(const QModelIndex &index);
|
||||
|
||||
public slots:
|
||||
void onClear();
|
||||
|
||||
signals:
|
||||
void send(const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName);
|
||||
|
||||
private:
|
||||
QString getString(const dds::core::xtypes::DynamicData &data, uint32_t member_index) const;
|
||||
QString getInfo(const dds::core::xtypes::DynamicData &data, const QString &topicName) const;
|
||||
QMap<QString, QString> getInfoInternal(const dds::core::xtypes::DynamicData &data, const QStringList &fields) const;
|
||||
|
||||
|
||||
QVector<TelegramData> m_data;
|
||||
QMap<QString, QStringList> m_infoList;
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
friend class TelegramSortModel;
|
||||
};
|
||||
|
||||
class TelegramSortModel final : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QSortFilterProxyModel::QSortFilterProxyModel;
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const override;
|
||||
};
|
||||
Executable
+201
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableView for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "telegramview.h"
|
||||
#include "telegrammodel.h"
|
||||
|
||||
#include "detailviewdlg.h"
|
||||
#include "nglsettings.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
#include <QFile>
|
||||
#include <QDomDocument>
|
||||
|
||||
TelegramView::TelegramView(QWidget *parent)
|
||||
: QTableView(parent)
|
||||
, m_proxyModel(new TelegramSortModel(this))
|
||||
, m_model(new TelegramModel(this))
|
||||
, m_bConnected(false)
|
||||
, m_logger(DdsView::getLogger("TelegramView"))
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void TelegramView::init()
|
||||
{
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
setModel(m_proxyModel);
|
||||
sortByColumn(0, Qt::AscendingOrder);
|
||||
|
||||
setAcceptDrops(true);
|
||||
setDragEnabled(true);
|
||||
setDragDropMode(QAbstractItemView::DragDrop);
|
||||
setDropIndicatorShown(true);
|
||||
setDefaultDropAction(Qt::CopyAction);
|
||||
|
||||
setSelectionBehavior(SelectRows);
|
||||
setSortingEnabled(true);
|
||||
horizontalHeader()->setStretchLastSection(true);
|
||||
// verticalHeader()->hide(); // show for visual counting
|
||||
|
||||
for (int i = 0; i < m_model->columnCount(QModelIndex()); ++i)
|
||||
resizeColumnToContents(i);
|
||||
|
||||
connect(this, &TelegramView::doubleClicked,
|
||||
this, &TelegramView::onTableDoubleClicked);
|
||||
connect(selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, &TelegramView::onTableClicked);
|
||||
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &TelegramView::customContextMenuRequested,
|
||||
this, &TelegramView::onCustomContextMenuRequested);
|
||||
connect(m_model, &TelegramModel::send,
|
||||
this, &TelegramView::send);
|
||||
}
|
||||
|
||||
TelegramView::~TelegramView()
|
||||
{
|
||||
delete m_model;
|
||||
delete m_proxyModel;
|
||||
}
|
||||
|
||||
void TelegramView::onCustomContextMenuRequested(const QPoint &p)
|
||||
{
|
||||
QMenu menu(this);
|
||||
QAction *a;
|
||||
|
||||
a = menu.addAction(tr("Delete"), this, &TelegramView::onDeleteIndex);
|
||||
// Only allow deleting, when at least one row is selected
|
||||
if (selectedIndexes().count() == 0)
|
||||
a->setDisabled(true);
|
||||
|
||||
menu.addAction(tr("Clear"), m_model, &TelegramModel::onClear);
|
||||
|
||||
a = menu.addAction(tr("Send"), this, &TelegramView::onSendIndex);
|
||||
// Only allow sending, when only one row is selected:
|
||||
if (selectedIndexes().count() > m_model->columnCount(QModelIndex()) ||
|
||||
selectedIndexes().count() == 0 ||
|
||||
!m_bConnected)
|
||||
a->setDisabled(true);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
a = menu.addAction(tr("Save selected"), this, &TelegramView::onSaveIndex);
|
||||
// Only allow saving, when at least one row is selected
|
||||
if (selectedIndexes().count() == 0)
|
||||
a->setDisabled(true);
|
||||
|
||||
a = menu.addAction(tr("Save all"), this, &TelegramView::onSaveAllIndexes);
|
||||
// Only allow saving all, when at least one row is available
|
||||
if (m_model->rowCount(QModelIndex()) == 0)
|
||||
a->setDisabled(true);
|
||||
|
||||
menu.exec(viewport()->mapToGlobal(p));
|
||||
}
|
||||
|
||||
void TelegramView::onDeleteIndex()
|
||||
{
|
||||
const auto selIndexes = selectedIndexes();
|
||||
for (const QModelIndex &mi : selIndexes)
|
||||
{
|
||||
if (mi.column() != 0)
|
||||
continue;
|
||||
QModelIndex sourceMi = m_proxyModel->mapToSource(mi);
|
||||
m_model->deleteRow(sourceMi);
|
||||
}
|
||||
}
|
||||
|
||||
void TelegramView::onSendIndex()
|
||||
{
|
||||
// Selected Indexes should be only those in exactly one row.
|
||||
// Todo: return after emit to not being able to send millions of samples?
|
||||
const auto selIndexes = selectedIndexes();
|
||||
for (const QModelIndex &mi : selIndexes)
|
||||
{
|
||||
if (mi.column() != 0)
|
||||
continue;
|
||||
const TelegramModel::TelegramData telegram = m_model->telegramData(m_proxyModel->mapToSource(mi));
|
||||
emit send(telegram.legatusData, telegram.topicName);
|
||||
}
|
||||
}
|
||||
|
||||
void TelegramView::onSaveIndex()
|
||||
{
|
||||
QVector<TelegramModel::TelegramData> vector;
|
||||
const auto selIndexes = selectedIndexes();
|
||||
for (const QModelIndex &mi : selIndexes)
|
||||
{
|
||||
if (mi.column() != 0)
|
||||
continue;
|
||||
vector.append(m_model->telegramData(m_proxyModel->mapToSource(mi)));
|
||||
}
|
||||
onSaveDynamicDataVector(vector);
|
||||
}
|
||||
|
||||
void TelegramView::onSaveAllIndexes()
|
||||
{
|
||||
// Iterate over the proxy model to preserve sorting!
|
||||
QVector<TelegramModel::TelegramData> vector;
|
||||
vector.reserve(m_proxyModel->rowCount());
|
||||
for (int i = 0; i < m_proxyModel->rowCount(); ++i)
|
||||
{
|
||||
QModelIndex mi = m_proxyModel->index(i, 0);
|
||||
QModelIndex sourceMi = m_proxyModel->mapToSource(mi);
|
||||
vector.append(m_model->telegramData(sourceMi));
|
||||
}
|
||||
onSaveDynamicDataVector(vector);
|
||||
}
|
||||
|
||||
void TelegramView::onSaveDynamicDataVector(const QVector<TelegramModel::TelegramData> &vector)
|
||||
{
|
||||
QString filename = QFileDialog::getSaveFileName(this, tr("Save Sample List"), QString(), tr("Sample List Files (*.spl);;All Files(*)"));
|
||||
QFile f(filename);
|
||||
if (!f.open(QFile::WriteOnly))
|
||||
return;
|
||||
QDomDocument doc(QLatin1String("Samples"));
|
||||
QDomElement root = doc.createElement(QLatin1String("Samples"));
|
||||
doc.appendChild(root);
|
||||
for (const TelegramModel::TelegramData &telegram : vector)
|
||||
telegram.toXml(doc, root);
|
||||
f.write(doc.toByteArray());
|
||||
f.close();
|
||||
}
|
||||
|
||||
void TelegramView::onTableDoubleClicked(const QModelIndex &mi)
|
||||
{
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> data = m_model->dynamicData(m_proxyModel->mapToSource(mi));
|
||||
const QString topicName = m_model->topicName(m_proxyModel->mapToSource(mi));
|
||||
DetailViewDlg *dtv = new DetailViewDlg(this);
|
||||
dtv->init(*data.data(), topicName, RwMode::ReadWrite);
|
||||
dtv->setBtnsSendClose();
|
||||
dtv->show();
|
||||
dtv->setConnected(m_bConnected);
|
||||
connect(dtv, &DetailViewDlg::send, this, &TelegramView::send);
|
||||
}
|
||||
|
||||
void TelegramView::onTableClicked(const QModelIndex &mi)
|
||||
{
|
||||
if (selectedIndexes().count() > m_model->columnCount(QModelIndex()) || !mi.isValid())
|
||||
{
|
||||
emit clearDetailView();
|
||||
return;
|
||||
}
|
||||
|
||||
const QSharedPointer<dds::core::xtypes::DynamicData> data = m_model->dynamicData(m_proxyModel->mapToSource(mi));
|
||||
emit dataClicked(*data.data(), m_model->topicName(m_proxyModel->mapToSource(mi)));
|
||||
}
|
||||
|
||||
|
||||
void TelegramView::onTypeCodeInfosChanged()
|
||||
{
|
||||
m_model->onTypeCodeInfosChanged();
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* TableView for dynamic topics
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QTableView>
|
||||
#include "telegrammodel.h"
|
||||
#include "logger.h"
|
||||
|
||||
class TelegramSortModel;
|
||||
class DetailViewDlg;
|
||||
|
||||
class TelegramView : public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TelegramView(QWidget *parent = nullptr);
|
||||
~TelegramView();
|
||||
|
||||
TelegramModel *telegramModel() {return m_model;}
|
||||
|
||||
void setConnected(bool bConnected) { m_bConnected = bConnected; }
|
||||
|
||||
signals:
|
||||
void dataClicked(const dds::core::xtypes::DynamicData &data, const QString &topicName);
|
||||
void clearDetailView();
|
||||
void send(const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName);
|
||||
|
||||
public slots:
|
||||
void onTypeCodeInfosChanged();
|
||||
|
||||
private:
|
||||
|
||||
void init();
|
||||
void onSaveDynamicDataVector(const QVector<TelegramModel::TelegramData> &vector);
|
||||
|
||||
TelegramSortModel *m_proxyModel;
|
||||
TelegramModel *m_model;
|
||||
bool m_bConnected;
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
private slots:
|
||||
void onTableClicked(const QModelIndex &mi);
|
||||
void onTableDoubleClicked(const QModelIndex &mi);
|
||||
void onCustomContextMenuRequested(const QPoint &p);
|
||||
void onDeleteIndex();
|
||||
void onSendIndex();
|
||||
void onSaveIndex();
|
||||
void onSaveAllIndexes();
|
||||
};
|
||||
Executable
+950
@@ -0,0 +1,950 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Tree model for the TopicSortModel
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "topic_tree_model.h"
|
||||
#include <QBuffer>
|
||||
#include <QDataStream>
|
||||
#include <QDateTime>
|
||||
#include <QImage>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QPolygonF>
|
||||
#include <QSet>
|
||||
#include <QTransform>
|
||||
#include <rti/core/xtypes/DynamicDataMemberInfo.hpp>
|
||||
#include <dds/core/xtypes/EnumType.hpp>
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
|
||||
// taken from AreaEffectSimulation::Icons::imageFromPolygon()
|
||||
QImage imageFromForm(const QPolygonF &poly, const int width)
|
||||
{
|
||||
if (poly.isEmpty())
|
||||
return QImage();
|
||||
QPolygonF polyInUtmCoords;
|
||||
polyInUtmCoords.reserve(poly.size());
|
||||
int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(poly.first()).zone;
|
||||
for (const QPointF &point : poly)
|
||||
{
|
||||
LtsCoordTrans::UtmCoordinate coord = LtsCoordTrans::UtmCoordinate::fromGeographic(point, zone);
|
||||
polyInUtmCoords << QPointF(coord.x, coord.y);
|
||||
}
|
||||
|
||||
const QPolygonF bounding = polyInUtmCoords.boundingRect();
|
||||
|
||||
// center to center / Todo:: Does it always work?
|
||||
const QTransform mTrans(QTransform::fromTranslate(-bounding.boundingRect().bottomLeft().x(), -bounding.boundingRect().bottomLeft().y()));
|
||||
|
||||
|
||||
qreal w = bounding.boundingRect().width();
|
||||
qreal h = bounding.boundingRect().height();
|
||||
double scale = w > h ? width / w : width / h;
|
||||
w *= scale;
|
||||
h *= scale;
|
||||
scale *= 0.96; // Leave 4% border (i.e. 2% each side)
|
||||
const QTransform mScale(QTransform::fromScale(scale, -scale));
|
||||
const QTransform mTransSmall(QTransform::fromTranslate(0.02 * width + (width - w) / 2, 0.02 * width + (width - h) / 2));
|
||||
|
||||
QImage img(width, width, QImage::Format_ARGB32);
|
||||
img.fill(0);
|
||||
|
||||
QPainter p(&img);
|
||||
p.fillRect(img.rect(), Qt::black);
|
||||
|
||||
QPolygonF mPoly = mTrans.map(polyInUtmCoords);
|
||||
mPoly = mScale.map(mPoly);
|
||||
mPoly = mTransSmall.map(mPoly);
|
||||
|
||||
|
||||
p.setBrush(QBrush(Qt::red, Qt::SolidPattern));
|
||||
p.setPen(QPen(p.brush(), 2));
|
||||
p.drawPolygon(mPoly);
|
||||
p.end();
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
TopicDataModel::TopicDataModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_data(nullptr)
|
||||
, m_logger(DdsView::getLogger("TopicDataModel"))
|
||||
{}
|
||||
|
||||
TopicDataModel::~TopicDataModel()
|
||||
{
|
||||
cleanupModelIndexes();
|
||||
}
|
||||
|
||||
void TopicDataModel::setTopicData(const dds::core::xtypes::DynamicData &data)
|
||||
{
|
||||
beginResetModel();
|
||||
cleanupModelIndexes();
|
||||
m_data = std::make_shared<dds::core::xtypes::DynamicData>(data);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TopicDataModel::reset()
|
||||
{
|
||||
beginResetModel();
|
||||
cleanupModelIndexes();
|
||||
m_data.reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TopicDataModel::cleanupModelIndexes()
|
||||
{
|
||||
for (const QModelIndex &idx : qAsConst(m_pathInformations))
|
||||
{
|
||||
IndexData *id = static_cast<IndexData *>(idx.internalPointer());
|
||||
delete id;
|
||||
}
|
||||
m_pathInformations.clear();
|
||||
}
|
||||
|
||||
std::shared_ptr<dds::core::xtypes::DynamicData> TopicDataModel::parentFromPath(int pathIdx, const QVector<int> &path,
|
||||
const std::shared_ptr<dds::core::xtypes::DynamicData> &ddParent) const
|
||||
{
|
||||
if (m_logger.isDebugEnabled())
|
||||
{
|
||||
QStringList sl;
|
||||
sl.reserve(path.size());
|
||||
for (int i : path)
|
||||
sl += QString::number(i);
|
||||
QString s = sl.join(QLatin1Char(','));
|
||||
DEBUG_LOG(m_logger) << "parentFromPath: " << s << " - " << pathIdx;
|
||||
}
|
||||
if (pathIdx == path.size() - 1)
|
||||
return ddParent;
|
||||
|
||||
const int row = path.at(pathIdx);
|
||||
const int memberCount = ddParent->member_count();
|
||||
|
||||
if (row < 0 || row >= memberCount)
|
||||
return std::shared_ptr<dds::core::xtypes::DynamicData>();
|
||||
|
||||
DEBUG_LOG(m_logger) << "mfp Trying to get member at idx " << row + 1;
|
||||
try
|
||||
{
|
||||
const rti::core::xtypes::DynamicDataMemberInfo mi = ddParent->member_info(row + 1);
|
||||
DEBUG_LOG(m_logger) << "Member name is " << mi.member_name().c_str();
|
||||
if (mi.member_kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE &&
|
||||
mi.member_kind() != dds::core::xtypes::TypeKind::SEQUENCE_TYPE &&
|
||||
mi.member_kind() != dds::core::xtypes::TypeKind::ARRAY_TYPE)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Member is not sequence/structure... something is wrong";
|
||||
return std::shared_ptr<dds::core::xtypes::DynamicData>();
|
||||
}
|
||||
std::shared_ptr<dds::core::xtypes::DynamicData> child;
|
||||
child.reset(new dds::core::xtypes::DynamicData(ddParent->value<dds::core::xtypes::DynamicData>(row + 1)));
|
||||
return parentFromPath(pathIdx + 1,
|
||||
path,
|
||||
child);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error trying to access child at " << row + 1;
|
||||
}
|
||||
return std::shared_ptr<dds::core::xtypes::DynamicData>();
|
||||
}
|
||||
|
||||
QString TopicDataModel::positionFromStruct(const dds::core::xtypes::DynamicData &data)
|
||||
{
|
||||
QString ret;
|
||||
if (data.type().name() == "Core::GeoPosition2D" ||
|
||||
data.type().name() == "Core::GeoPosition" ||
|
||||
data.type().name() == "Core::GeoPositionRelativeTime" ||
|
||||
data.type().name() == "Core::GeoPositionAbsoluteTime")
|
||||
{
|
||||
const double lon = data.value<double>("lon");
|
||||
const double lat = data.value<double>("lat");
|
||||
LtsCoordTrans::UtmCoordinate coord(LtsCoordTrans::UtmCoordinate::fromGeographic(lon, lat));
|
||||
ret = coord.mgrs();
|
||||
if (ret.isEmpty())
|
||||
ret = tr("invalid pos");
|
||||
}
|
||||
|
||||
if (data.type().name() == "Core::GeoPosition" ||
|
||||
data.type().name() == "Core::GeoPositionRelativeTime" ||
|
||||
data.type().name() == "Core::GeoPositionAbsoluteTime")
|
||||
{
|
||||
const double z = data.value<double>("z");
|
||||
// ret.append( QString(QStringLiteral(" ; z=%1")).arg( QString::number(z, 'f', 2) ) );
|
||||
ret.append( QString(QStringLiteral(" ; z=%1")).arg( z, 9, 'f', 2, QLatin1Char(' ')) );
|
||||
}
|
||||
|
||||
if (data.type().name() == "Core::GeoPositionRelativeTime")
|
||||
{
|
||||
const long long relativeTime = data.value<long long>("relativeTime");
|
||||
ret.append( QString(QStringLiteral(" ; relTime=%1")).arg(relativeTime) );
|
||||
} else if (data.type().name() == "Core::GeoPositionAbsoluteTime")
|
||||
{
|
||||
const long long absoluteTime = data.value<long long>("absoluteTime");
|
||||
ret.append( QString(QStringLiteral(" ; absTime=%1")).arg(absoluteTime) );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QVariant TopicDataModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return dataInternal(index, role);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error retrieving data for " << index.row()
|
||||
<< "/" << index.column() << ": " << ex.what();
|
||||
}
|
||||
return QVariant();
|
||||
|
||||
}
|
||||
|
||||
bool findTypeFromStructType(const dds::core::xtypes::StructType &parentIn,
|
||||
dds::core::xtypes::StructType &parentOut,
|
||||
size_t &row)
|
||||
{
|
||||
if (parentIn.has_parent())
|
||||
{
|
||||
if (findTypeFromStructType(parentIn.parent(), parentOut, row))
|
||||
return true;
|
||||
}
|
||||
if (parentIn.member_count() > row)
|
||||
{
|
||||
parentOut = parentIn;
|
||||
return true;
|
||||
}
|
||||
row -= parentIn.member_count();
|
||||
return false;
|
||||
}
|
||||
|
||||
const dds::core::xtypes::Member &TopicDataModel::memberFromType(const dds::core::xtypes::StructType &parent,
|
||||
dds::core::xtypes::StructType &stCopy,
|
||||
int row)
|
||||
{
|
||||
size_t sRow = row;
|
||||
findTypeFromStructType(parent, stCopy, sRow);
|
||||
return stCopy.member(sRow);
|
||||
}
|
||||
|
||||
QVariant TopicDataModel::dataInternal(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!m_data)
|
||||
return QVariant();
|
||||
if (role != Qt::DisplayRole &&
|
||||
role != Qt::ToolTipRole)
|
||||
return QVariant();
|
||||
if (index.row() < 0 || index.column() < 0 || index.column() >= columnCount(index))
|
||||
return QVariant();
|
||||
|
||||
IndexData *id = static_cast<IndexData *>(index.internalPointer());
|
||||
std::shared_ptr<dds::core::xtypes::DynamicData> ddParent = parentFromPath(0, id->path, m_data);
|
||||
if (!ddParent)
|
||||
return QVariant();
|
||||
|
||||
const int memberCount = ddParent->member_count();
|
||||
if (index.row() >= memberCount)
|
||||
return QVariant();
|
||||
|
||||
const int row = id->path.last() + 1;
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
QVariant v = id->displayRole[index.column()];
|
||||
if (v.isValid())
|
||||
return v;
|
||||
switch (index.column())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (ddParent->type_kind() == dds::core::xtypes::TypeKind::SEQUENCE_TYPE)
|
||||
{
|
||||
const dds::core::xtypes::SequenceType dtParent = static_cast<const dds::core::xtypes::SequenceType &>(ddParent->type());
|
||||
const dds::core::xtypes::DynamicType &childType = dtParent.content_type();
|
||||
if (childType.kind() == dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
v = QStringLiteral("%1 [%2]").arg(QString::fromStdString(childType.name())).arg(row - 1);
|
||||
else
|
||||
{
|
||||
// PODs
|
||||
QString typeName;
|
||||
switch (childType.kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
typeName = QStringLiteral("char");
|
||||
break;
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
case dds::core::xtypes::TypeKind::CHAR_32_TYPE:
|
||||
#else
|
||||
case dds::core::xtypes::TypeKind::CHAR_16_TYPE:
|
||||
#endif
|
||||
typeName = QStringLiteral("wchar_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
typeName = QStringLiteral("uint8_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
typeName = QStringLiteral("int16_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
typeName = QStringLiteral("uint16_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
typeName = QStringLiteral("int32_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
typeName = QStringLiteral("uint32_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
typeName = QStringLiteral("int64_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
typeName = QStringLiteral("uint64_t");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
typeName = QStringLiteral("float");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
typeName = QStringLiteral("double");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
typeName = QStringLiteral("bool");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
typeName = QStringLiteral("string");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
typeName = QStringLiteral("enum");
|
||||
break;
|
||||
default:
|
||||
typeName = QStringLiteral("0x") + QString::number(childType.kind().underlying(), 16);
|
||||
break;
|
||||
}
|
||||
v = QStringLiteral("%1 [%2]").arg(typeName).arg(row);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const dds::core::xtypes::StructType &dtParent = static_cast<const dds::core::xtypes::StructType &>(ddParent->type());
|
||||
dds::core::xtypes::StructType dtParentCopy = dtParent;
|
||||
const dds::core::xtypes::Member &child = memberFromType(dtParent, dtParentCopy, row - 1);
|
||||
v = QString::fromStdString(child.name());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (!ddParent->member_exists(row))
|
||||
return tr("<not set>");
|
||||
const rti::core::xtypes::DynamicDataMemberInfo mi = ddParent->member_info(row);
|
||||
switch (mi.member_kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
v = QChar::fromLatin1(ddParent->value<char>(row));
|
||||
break;
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
case dds::core::xtypes::TypeKind::CHAR_32_TYPE:
|
||||
#else
|
||||
case dds::core::xtypes::TypeKind::CHAR_16_TYPE:
|
||||
#endif
|
||||
v = QChar(ddParent->value<DDS_Wchar>(row));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
v = QVariant::fromValue(ddParent->value<uint8_t>(row)).toUInt();
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
v = QVariant::fromValue(ddParent->value<int16_t>(row));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
v = QVariant::fromValue(ddParent->value<uint16_t>(row));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
v = QVariant::fromValue(ddParent->value<int32_t>(row));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
v = QVariant::fromValue(ddParent->value<uint32_t>(row));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
v = QVariant::fromValue(qint64(ddParent->value<int64_t>(row)));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
v = QVariant::fromValue(quint64(ddParent->value<uint64_t>(row)));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
v = QString::number(ddParent->value<float>(row), 'f', 4);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
v = QString::number(ddParent->value<double>(row), 'f', 8);
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
v = ddParent->value<bool>(row) ? tr("true") : tr("false");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
const dds::core::string tmp(ddParent->value<dds::core::string>(row));
|
||||
v = QString::fromUtf8(tmp.c_str(), tmp.size());
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE:
|
||||
{
|
||||
QString ret = positionFromStruct(ddParent->value<dds::core::xtypes::DynamicData>(row));
|
||||
if (!ret.isEmpty())
|
||||
v = ret;
|
||||
else
|
||||
v = tr("[struct]");
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
case dds::core::xtypes::TypeKind::ARRAY_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::DynamicData childStructData = ddParent->value<dds::core::xtypes::DynamicData>(row);
|
||||
v = tr("cnt: %1").arg(childStructData.member_count());
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
{
|
||||
// types are not plain - we have to search for the member in the type-tree
|
||||
dds::core::xtypes::StructType structTypeParent = static_cast<const dds::core::xtypes::StructType &>(ddParent->type());
|
||||
const int ordinal = ddParent->value<int32_t>(row);
|
||||
unsigned int rowInType = structTypeParent.find_member_by_name(mi.member_name());
|
||||
while (rowInType == dds::core::xtypes::EnumType::INVALID_INDEX)
|
||||
{
|
||||
if (!structTypeParent.has_parent())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Could not retrieve enum value from Struct Type";
|
||||
return QStringLiteral("<%1>").arg(ordinal);
|
||||
}
|
||||
structTypeParent = structTypeParent.parent();
|
||||
rowInType = structTypeParent.find_member_by_name(mi.member_name());
|
||||
}
|
||||
const dds::core::xtypes::Member &enumMember = structTypeParent.member(rowInType); // index in struct != index in data!
|
||||
const dds::core::xtypes::EnumType &inner = static_cast<const dds::core::xtypes::EnumType &>(enumMember.type());
|
||||
v = QStringLiteral("%1 (%2)").arg(QString::fromStdString(inner.name())).arg(ordinal);
|
||||
for (size_t i = 0; i < inner.member_count(); ++i)
|
||||
{
|
||||
const auto &member = inner.member(i);
|
||||
if (member.ordinal() == ordinal)
|
||||
{
|
||||
v = QStringLiteral("%1 (%2)").arg(QString::fromStdString(member.name())).arg(ordinal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
v = QString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
const dds::core::xtypes::TypeKind tk = childTypeKind(ddParent, row - 1);
|
||||
switch (tk.underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE:
|
||||
v = tr("struct");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
v = tr("sequence");
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::ARRAY_TYPE:
|
||||
v = tr("array");
|
||||
break;
|
||||
default:
|
||||
v = QStringLiteral("-");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
v = ddParent->member_exists(row) ? QStringLiteral("1") : QStringLiteral("0");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
id->displayRole[index.column()] = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
if (role == Qt::ToolTipRole)
|
||||
{
|
||||
if (!ddParent->member_exists(row))
|
||||
return QVariant();
|
||||
const rti::core::xtypes::DynamicDataMemberInfo mi = ddParent->member_info(row);
|
||||
if (mi.member_kind() == dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
{
|
||||
const QString ret = positionFromStruct(ddParent->value<dds::core::xtypes::DynamicData>(row));
|
||||
if (!ret.isEmpty())
|
||||
return ret;
|
||||
}
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::SEQUENCE_TYPE &&
|
||||
mi.member_name() == "form")
|
||||
{
|
||||
// assume it is a form from aes with lat/lon values
|
||||
try
|
||||
{
|
||||
const dds::core::xtypes::DynamicData childStructData = ddParent->value<dds::core::xtypes::DynamicData>(row);
|
||||
QPolygonF poly;
|
||||
poly.reserve(childStructData.member_count());
|
||||
for (unsigned int i = 1; i <= childStructData.member_count(); ++i)
|
||||
{
|
||||
const auto data = childStructData.value<dds::core::xtypes::DynamicData>(i);
|
||||
poly += QPointF(data.value<double>("lon"), data.value<double>("lat"));
|
||||
}
|
||||
QImage img = imageFromForm(poly, 64);
|
||||
QBuffer buf;
|
||||
buf.open(QIODevice::ReadWrite);
|
||||
img.save(&buf, "PNG");
|
||||
return QStringLiteral("<img src='data:image/png;base64,%1'>").arg(QString::fromLatin1(buf.data().toBase64()));
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// maybe no aes item view
|
||||
}
|
||||
}
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_8_TYPE)
|
||||
{
|
||||
const qint64 val = ddParent->value<uint8_t>(row);
|
||||
return QStringLiteral("0x%1\n0b%2").arg(val, 2, 16, QLatin1Char('0'))
|
||||
.arg(val, 8, 2, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::INT_16_TYPE)
|
||||
{
|
||||
const qint64 val = ddParent->value<int16_t>(row);
|
||||
return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_16_TYPE)
|
||||
{
|
||||
const quint64 val = ddParent->value<uint16_t>(row);
|
||||
return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::INT_32_TYPE)
|
||||
{
|
||||
const qint64 val = ddParent->value<int32_t>(row);
|
||||
return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_32_TYPE)
|
||||
{
|
||||
const quint64 val = ddParent->value<uint32_t>(row);
|
||||
return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::INT_64_TYPE)
|
||||
{
|
||||
// we assume it's a timestamp when its > 946681200000 (> 01.01.2000)
|
||||
const qint64 val = ddParent->value<int64_t>(row);
|
||||
if (val > 946681200000)
|
||||
{
|
||||
const QDateTime dt(QDateTime::fromMSecsSinceEpoch(val).toUTC());
|
||||
return dt.toString(QStringLiteral("dd.MM.yy - hh:mm:ss.zzz Z"));
|
||||
}
|
||||
return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
else if (mi.member_kind() == dds::core::xtypes::TypeKind::UINT_64_TYPE)
|
||||
{
|
||||
const quint64 val = ddParent->value<uint64_t>(row);
|
||||
return QStringLiteral("0x%1").arg(val, 8, 16, QLatin1Char('0'));
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant TopicDataModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
|
||||
return QAbstractItemModel::headerData(section, orientation, role);
|
||||
switch (section)
|
||||
{
|
||||
case 0:
|
||||
return tr("Name");
|
||||
case 1:
|
||||
return tr("Value");
|
||||
case 2:
|
||||
return tr("Kind");
|
||||
case 3:
|
||||
return tr("Exists");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QAbstractItemModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
int TopicDataModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (!m_data)
|
||||
return 0;
|
||||
// root knot
|
||||
if (!parent.isValid())
|
||||
return m_data->member_count();
|
||||
|
||||
const IndexData *id = static_cast<const IndexData *>(parent.internalPointer());
|
||||
std::shared_ptr<dds::core::xtypes::DynamicData> ddParent = parentFromPath(0, id->path, m_data);
|
||||
if (!ddParent)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "rowCount - can not determine count!";
|
||||
return 0;
|
||||
}
|
||||
try
|
||||
{
|
||||
const int row = id->path.last() + 1;
|
||||
const dds::core::xtypes::TypeKind tk = childTypeKind(ddParent, row - 1);
|
||||
if (tk == dds::core::xtypes::TypeKind::STRUCTURE_TYPE ||
|
||||
tk == dds::core::xtypes::TypeKind::SEQUENCE_TYPE ||
|
||||
tk == dds::core::xtypes::TypeKind::ARRAY_TYPE)
|
||||
{
|
||||
if (!ddParent->member_exists(row)) // optional, not set
|
||||
return 0;
|
||||
const dds::core::xtypes::DynamicData childStructData = ddParent->value<dds::core::xtypes::DynamicData>(row);
|
||||
return childStructData.member_count();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error trying to access child at " << parent.row() + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TopicDataModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return 4;
|
||||
}
|
||||
|
||||
QModelIndex TopicDataModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
const PathInformationKey pik({row, column, parent});
|
||||
auto it = m_pathInformations.find(pik);
|
||||
if (it == m_pathInformations.end())
|
||||
{
|
||||
IndexData *id = new IndexData;
|
||||
id->index = createIndex(row, column, id);
|
||||
if (parent.isValid())
|
||||
id->path = static_cast<IndexData *>(parent.internalPointer())->path;
|
||||
id->path.push_back(row);
|
||||
id->parent = parent;
|
||||
m_pathInformations.insert(pik, id->index);
|
||||
return id->index;
|
||||
}
|
||||
return it.value();
|
||||
}
|
||||
|
||||
QModelIndex TopicDataModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
if (!child.isValid())
|
||||
return QModelIndex();
|
||||
const IndexData *id = static_cast<const IndexData *>(child.internalPointer());
|
||||
return id->parent;
|
||||
}
|
||||
|
||||
dds::core::xtypes::TypeKind TopicDataModel::childTypeKind(const std::shared_ptr<dds::core::xtypes::DynamicData> &ddParent,
|
||||
int typeRow)
|
||||
{
|
||||
switch (ddParent->type_kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
case dds::core::xtypes::TypeKind::ARRAY_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::CollectionType dtParent = static_cast<const dds::core::xtypes::CollectionType &>(ddParent->type());
|
||||
const dds::core::xtypes::DynamicType &childType = dtParent.content_type();
|
||||
return childType.kind();
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::StructType &dtParent = static_cast<const dds::core::xtypes::StructType &>(ddParent->type());
|
||||
dds::core::xtypes::StructType dtCopy(dtParent);
|
||||
const dds::core::xtypes::Member &child = memberFromType(dtParent, dtCopy, typeRow);
|
||||
const dds::core::xtypes::DynamicType &childType = child.type();
|
||||
return childType.kind();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return dds::core::xtypes::TypeKind::NO_TYPE;
|
||||
}
|
||||
|
||||
//
|
||||
// TopicSortModel
|
||||
//
|
||||
TreeItem *TopicSortModel::addData(const TopicDescriptionEx &data, TreeItem *parent)
|
||||
{
|
||||
return m_sourceModel->addData(data, parent);
|
||||
}
|
||||
|
||||
TopicDescriptionEx TopicSortModel::getData(const QModelIndex &idx) const
|
||||
{
|
||||
return m_sourceModel->getData(mapToSource(idx));
|
||||
}
|
||||
|
||||
void TopicSortModel::setSourceModel(QAbstractItemModel *sourceModel)
|
||||
{
|
||||
m_sourceModel = qobject_cast<TreeModel *>(sourceModel);
|
||||
QSortFilterProxyModel::setSourceModel(sourceModel);
|
||||
}
|
||||
|
||||
bool TopicSortModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||
{
|
||||
if (source_left.column() != source_right.column())
|
||||
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||
TopicDescriptionEx descriptionLeft;
|
||||
TopicDescriptionEx descriptionRight;
|
||||
// const TopicDescriptionEx &tdL = m_sourceModel->data(source_left, Qt::DisplayRole); // Todo: Korrekte Role
|
||||
// const TopicDescriptionEx &tdR = m_sourceModel->data(source_right, Qt::DisplayRole); // s.o.
|
||||
descriptionLeft.serviceName = m_sourceModel->data(source_left, Qt::DisplayRole).toString(); // Todo: Korrekte Role
|
||||
descriptionRight.serviceName = m_sourceModel->data(source_right, Qt::DisplayRole).toString(); // s.o.
|
||||
const TopicDescriptionEx &tdL = descriptionLeft;
|
||||
const TopicDescriptionEx &tdR = descriptionRight;
|
||||
if (tdL.serviceName == tdR.serviceName)
|
||||
return tdL.name < tdR.name;
|
||||
return tdL.serviceName < tdR.serviceName;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// | TreeItem |
|
||||
// --------------------------------------------------------------------------------
|
||||
// Constructor
|
||||
TreeItem::TreeItem(const TopicDescriptionEx &description, TreeItem *parent)
|
||||
: m_data(description)
|
||||
, m_parentItem(parent)
|
||||
{}
|
||||
|
||||
//Destructor
|
||||
TreeItem::~TreeItem()
|
||||
{
|
||||
qDeleteAll(m_childItems);
|
||||
}
|
||||
|
||||
void TreeItem::appendChild(TreeItem *child)
|
||||
{
|
||||
m_childItems.append(child);
|
||||
}
|
||||
|
||||
// Getter
|
||||
TreeItem *TreeItem::child(int row)
|
||||
{
|
||||
if (row < 0 || row >= m_childItems.size())
|
||||
return nullptr;
|
||||
return m_childItems.value(row);
|
||||
}
|
||||
|
||||
int TreeItem::childCount() const
|
||||
{
|
||||
return m_childItems.count();
|
||||
}
|
||||
|
||||
QVariant TreeItem::itemData(int column) const
|
||||
{
|
||||
if (column == 0)
|
||||
{
|
||||
if (m_data.isEmpty())
|
||||
return m_data.serviceName;
|
||||
return QString::fromStdString(m_data.name).split(QStringLiteral("::")).last();
|
||||
}
|
||||
else if (column == 1)
|
||||
{
|
||||
if (m_data.isEmpty())
|
||||
return QVariant();
|
||||
return QString::fromStdString(m_data.typeName);
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
TreeItem *TreeItem::parentItem()
|
||||
{
|
||||
return m_parentItem;
|
||||
}
|
||||
|
||||
int TreeItem::row() const
|
||||
{
|
||||
if (m_parentItem)
|
||||
return m_parentItem->m_childItems.indexOf(const_cast<TreeItem *>(this));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// | TreeModel |
|
||||
// --------------------------------------------------------------------------------
|
||||
// Constructor
|
||||
TreeModel::TreeModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_bWithTypeCode(false)
|
||||
{
|
||||
TopicDescriptionEx desc;
|
||||
desc.serviceName = QLatin1String("Title");
|
||||
m_rootItem = new TreeItem(desc, nullptr);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
TreeModel::~TreeModel()
|
||||
{
|
||||
delete m_rootItem;
|
||||
}
|
||||
|
||||
// Getter
|
||||
int TreeModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return m_bWithTypeCode ? 2 : 1;
|
||||
}
|
||||
|
||||
QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
QMimeData *md = new QMimeData();
|
||||
QByteArray encodedData;
|
||||
QDataStream stream(&encodedData, QIODevice::WriteOnly);
|
||||
for (const QModelIndex &idx : indexes)
|
||||
{
|
||||
if (idx.column() != 0)
|
||||
continue;
|
||||
const TopicDescriptionEx td = getData(idx);
|
||||
stream << td.name.c_str();
|
||||
stream << QByteArray(td.typeName.c_str());
|
||||
}
|
||||
md->setData(QLatin1String("Text/TopicName+TypeName"), encodedData);
|
||||
return md;
|
||||
}
|
||||
|
||||
QStringList TreeModel::mimeTypes() const
|
||||
{
|
||||
return QStringList() << QLatin1String("Text/TopicName+TypeName");
|
||||
}
|
||||
|
||||
Qt::DropActions TreeModel::supportedDragActions() const
|
||||
{
|
||||
return Qt::CopyAction;
|
||||
}
|
||||
|
||||
QVariant TreeModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
|
||||
|
||||
return item->itemData(index.column());
|
||||
}
|
||||
|
||||
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
|
||||
Qt::ItemFlags f = QAbstractItemModel::flags(index);
|
||||
if (index.parent().isValid())
|
||||
f |= Qt::ItemIsDragEnabled;
|
||||
else
|
||||
f &= ~Qt::ItemIsSelectable;
|
||||
return f;
|
||||
}
|
||||
|
||||
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
|
||||
int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
return m_rootItem->itemData(section);
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
return QModelIndex();
|
||||
|
||||
TreeItem *parentItem;
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = m_rootItem;
|
||||
else
|
||||
parentItem = static_cast<TreeItem *>(parent.internalPointer());
|
||||
|
||||
TreeItem *childItem = parentItem->child(row);
|
||||
if (childItem)
|
||||
return createIndex(row, column, childItem);
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
TreeItem *childItem = static_cast<TreeItem *>(index.internalPointer());
|
||||
TreeItem *parentItem = childItem->parentItem();
|
||||
|
||||
if (!parentItem || parentItem == m_rootItem)
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
|
||||
int TreeModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
TreeItem *parentItem;
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = m_rootItem;
|
||||
else
|
||||
parentItem = static_cast<TreeItem *>(parent.internalPointer());
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
TreeItem *TreeModel::addData(const TopicDescriptionEx &data, TreeItem *parent)
|
||||
{
|
||||
if (!parent)
|
||||
{
|
||||
parent = m_rootItem;
|
||||
bool createSubKnot = true;
|
||||
for (int i = 0; i < m_rootItem->childCount(); ++i)
|
||||
{
|
||||
if (m_rootItem->child(i)->itemData(0).toString() == data.serviceName)
|
||||
{
|
||||
parent = m_rootItem->child(i);
|
||||
createSubKnot = false; // already there -> no need to create another one
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (createSubKnot)
|
||||
{
|
||||
TopicDescriptionEx description;
|
||||
description.serviceName = data.serviceName;
|
||||
|
||||
beginInsertRows(QModelIndex(), parent->childCount(), parent->childCount());
|
||||
parent->appendChild(new TreeItem(description, parent));
|
||||
endInsertRows();
|
||||
parent = parent->child(parent->childCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
const QModelIndex parentIdx(createIndex(parent->row(), 0, parent));
|
||||
beginInsertRows(parentIdx, parent->childCount(), parent->childCount());
|
||||
TreeItem *ti(new TreeItem(data, parent));
|
||||
parent->appendChild(ti);
|
||||
endInsertRows();
|
||||
return ti;
|
||||
}
|
||||
|
||||
TopicDescriptionEx TreeModel::getData(const QModelIndex &idx) const
|
||||
{
|
||||
TreeItem *item = static_cast<TreeItem *>(idx.internalPointer());
|
||||
if (!item)
|
||||
return TopicDescriptionEx();
|
||||
return item->data();
|
||||
}
|
||||
Executable
+172
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Tree model for the TopicSortModel
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QModelIndex>
|
||||
#include "logger.h"
|
||||
#include "nglsettings.h"
|
||||
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
|
||||
class TreeItem;
|
||||
class TreeModel;
|
||||
|
||||
class TopicDataModel final : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TopicDataModel(QObject *parent = nullptr);
|
||||
~TopicDataModel();
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
|
||||
static dds::core::xtypes::TypeKind typeKindFromData(const std::shared_ptr<dds::core::xtypes::DynamicData> &ddParent,
|
||||
int row);
|
||||
static const dds::core::xtypes::Member &memberFromType(const dds::core::xtypes::StructType &parent,
|
||||
dds::core::xtypes::StructType &stCopy,
|
||||
int row);
|
||||
public Q_SLOTS:
|
||||
void setTopicData(const dds::core::xtypes::DynamicData &data);
|
||||
void reset();
|
||||
private:
|
||||
QVariant dataInternal(const QModelIndex &index, int role) const;
|
||||
void cleanupModelIndexes();
|
||||
std::shared_ptr<dds::core::xtypes::DynamicData> parentFromPath(int pathIdx, const QVector<int> &path,
|
||||
const std::shared_ptr<dds::core::xtypes::DynamicData> &ddParent) const;
|
||||
static QString positionFromStruct(const dds::core::xtypes::DynamicData &data);
|
||||
static dds::core::xtypes::TypeKind childTypeKind(const std::shared_ptr<dds::core::xtypes::DynamicData> &ddParent,
|
||||
int typeRow);
|
||||
private:
|
||||
std::shared_ptr<dds::core::xtypes::DynamicData> m_data;
|
||||
struct PathInformationKey
|
||||
{
|
||||
int row = 0;
|
||||
int column = 0;
|
||||
QModelIndex parentIndex;
|
||||
PathInformationKey(int r, int c, const QModelIndex &parent)
|
||||
: row(r)
|
||||
, column(c)
|
||||
, parentIndex(parent)
|
||||
{}
|
||||
bool operator==(const PathInformationKey &o) const
|
||||
{
|
||||
if (row != o.row)
|
||||
return false;
|
||||
if (column != o.column)
|
||||
return false;
|
||||
if (parentIndex != o.parentIndex)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
friend uint qHash(const TopicDataModel::PathInformationKey &k);
|
||||
|
||||
mutable QHash<PathInformationKey, QModelIndex> m_pathInformations;
|
||||
struct IndexData
|
||||
{
|
||||
QVector<int> path;
|
||||
QModelIndex index;
|
||||
QModelIndex parent;
|
||||
QVariant displayRole[4];
|
||||
};
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
inline uint qHash(const TopicDataModel::PathInformationKey &k)
|
||||
{
|
||||
const quint64 p = reinterpret_cast<quint64>(k.parentIndex.internalPointer());
|
||||
uint ret = static_cast<uint>(k.row);
|
||||
ret ^= static_cast<uint>(k.column);
|
||||
ret ^= static_cast<uint>(p & 0xffffffff);
|
||||
ret ^= static_cast<uint>((p >> 32) & 0xffffffff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// | Class TopicSortModel |
|
||||
// --------------------------------------------------------------------------------
|
||||
class TopicSortModel final : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QSortFilterProxyModel::QSortFilterProxyModel;
|
||||
|
||||
TreeItem *addData(const TopicDescriptionEx &data, TreeItem *parent);
|
||||
TopicDescriptionEx getData(const QModelIndex &idx) const;
|
||||
|
||||
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
||||
private:
|
||||
TreeModel *m_sourceModel = nullptr;
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// | Class TreeItem |
|
||||
// --------------------------------------------------------------------------------
|
||||
class TreeItem final
|
||||
{
|
||||
public:
|
||||
explicit TreeItem(const TopicDescriptionEx &description, TreeItem *parent);
|
||||
~TreeItem();
|
||||
|
||||
void appendChild(TreeItem *child);
|
||||
|
||||
TreeItem *child(int row);
|
||||
int childCount() const;
|
||||
QVariant itemData(int column) const;
|
||||
int row() const;
|
||||
TreeItem *parentItem();
|
||||
TopicDescriptionEx data() const { return m_data; }
|
||||
|
||||
private:
|
||||
TopicDescriptionEx m_data;
|
||||
QVector<TreeItem *> m_childItems;
|
||||
TreeItem *m_parentItem;
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// | Class TreeModel |
|
||||
// --------------------------------------------------------------------------------
|
||||
class TreeModel final : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TreeModel(QObject *parent = nullptr);
|
||||
~TreeModel();
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
QStringList mimeTypes() const override;
|
||||
Qt::DropActions supportedDragActions() const override;
|
||||
|
||||
TreeItem *addData(const TopicDescriptionEx &data, TreeItem *parent);
|
||||
TreeItem *rootItem() const { return m_rootItem; }
|
||||
TopicDescriptionEx getData(const QModelIndex &idx) const;
|
||||
void setWithTypeCode(bool bWith) { m_bWithTypeCode = bWith; }
|
||||
private:
|
||||
TreeItem *m_rootItem;
|
||||
bool m_bWithTypeCode;
|
||||
};
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Select Topic data for editing/setting default display data
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "topicselector.h"
|
||||
#include "typecodeselectordlg.h"
|
||||
#include "nglsettings.h"
|
||||
#include "topic_tree_model.h"
|
||||
#include "ui_topicselector.h"
|
||||
|
||||
TopicSelector::TopicSelector(Usage usage, QWidget *parent)
|
||||
: NGLDialog(parent)
|
||||
, m_typeCodeInfos(NGLSettings::instance()->infoValues())
|
||||
, m_ui(new Ui::TopicSelector)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
case Usage::SampleSkeletons:
|
||||
m_ui->nglDialogButtonBox->setStandardButtons(NGLStandardButton::Add | NGLStandardButton::Close);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onAdd, this, &TopicSelector::onAdd);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onClose, this, &TopicSelector::accept);
|
||||
connect(m_ui->tvDataTopics, &QTreeView::doubleClicked, this, &TopicSelector::onAdd);
|
||||
connect(m_ui->tvFunctionTopics, &QTreeView::doubleClicked, this, &TopicSelector::onAdd);
|
||||
setWindowTitle(tr("Select Topic..."));
|
||||
|
||||
break;
|
||||
case Usage::TypeCodeInfos:
|
||||
m_ui->nglDialogButtonBox->setStandardButtons(NGLStandardButton::Ok | NGLStandardButton::Cancel | NGLStandardButton::Edit);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onEdit, this, &TopicSelector::onEdit);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onCancel, this, &TopicSelector::onCancel);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onOk, this, &TopicSelector::onOk);
|
||||
connect(m_ui->tvDataTopics, &QTreeView::doubleClicked, this, &TopicSelector::onEdit);
|
||||
connect(m_ui->tvFunctionTopics, &QTreeView::doubleClicked, this, &TopicSelector::onEdit);
|
||||
// Todo: Better name...
|
||||
setWindowTitle(tr("Select Topic..."));
|
||||
break;
|
||||
}
|
||||
m_treeSrcModelData = new TreeModel(this);
|
||||
m_treeSrcModelData->setWithTypeCode(true);
|
||||
m_dataSortModel = new TopicSortModel(this);
|
||||
m_dataSortModel->setSourceModel(m_treeSrcModelData);
|
||||
m_ui->tvDataTopics->setModel(m_dataSortModel);
|
||||
|
||||
m_treeSrcModelFunction = new TreeModel(this);
|
||||
m_treeSrcModelFunction->setWithTypeCode(true);
|
||||
m_functionSortModel = new TopicSortModel(this);
|
||||
m_functionSortModel->setSourceModel(m_treeSrcModelFunction);
|
||||
m_ui->tvFunctionTopics->setModel(m_functionSortModel);
|
||||
|
||||
// QString = topicName (NOT typeName!)
|
||||
const QMap<std::string, TopicDescriptionEx> dataTopicMap = NGLSettings::instance()->dataTopics();
|
||||
for (auto it = dataTopicMap.constBegin(); it != dataTopicMap.constEnd(); ++it)
|
||||
m_dataSortModel->addData(*it, nullptr);
|
||||
m_dataSortModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
// QString = topicName (NOT typeName!)
|
||||
const QMap<std::string, TopicDescriptionEx> functionTopicMap = NGLSettings::instance()->functionTopics();
|
||||
for (auto it = functionTopicMap.constBegin(); it != functionTopicMap.constEnd(); ++it)
|
||||
m_functionSortModel->addData(*it, nullptr);
|
||||
m_functionSortModel->sort(0, Qt::AscendingOrder);
|
||||
m_ui->toolBox->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
TopicSelector::~TopicSelector()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void TopicSelector::onAdd()
|
||||
{
|
||||
TopicDescriptionEx topicDesc;
|
||||
if (m_ui->toolBox->currentWidget() == m_ui->tvFunctionTopics)
|
||||
{
|
||||
const QModelIndexList mil = m_ui->tvFunctionTopics->selectionModel()->selectedRows();
|
||||
if (mil.isEmpty())
|
||||
return;
|
||||
topicDesc = m_functionSortModel->getData(mil.at(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
const QModelIndexList mil = m_ui->tvDataTopics->selectionModel()->selectedRows();
|
||||
if (mil.isEmpty())
|
||||
return;
|
||||
topicDesc = m_dataSortModel->getData(mil.at(0));
|
||||
}
|
||||
emit data(dds::core::xtypes::DynamicData(topicDesc.type), QString::fromStdString(topicDesc.name));
|
||||
}
|
||||
|
||||
void TopicSelector::onEdit()
|
||||
{
|
||||
TopicDescriptionEx topicDesc;
|
||||
if (m_ui->toolBox->currentWidget() == m_ui->tvFunctionTopics)
|
||||
{
|
||||
const QModelIndexList mil = m_ui->tvFunctionTopics->selectionModel()->selectedRows();
|
||||
if (mil.isEmpty())
|
||||
return;
|
||||
topicDesc = m_functionSortModel->getData(mil.at(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
const QModelIndexList mil = m_ui->tvDataTopics->selectionModel()->selectedRows();
|
||||
if (mil.isEmpty())
|
||||
return;
|
||||
topicDesc = m_dataSortModel->getData(mil.at(0));
|
||||
}
|
||||
|
||||
if (m_dialogs.contains(topicDesc.typeName))
|
||||
{
|
||||
m_dialogs[topicDesc.typeName]->raise();
|
||||
return;
|
||||
}
|
||||
|
||||
TypeCodeSelectorDlg *tcsd = new TypeCodeSelectorDlg(this);
|
||||
connect(tcsd, &TypeCodeSelectorDlg::typeCodeInfosChanged,
|
||||
this, &TopicSelector::onTypeCodeInfosChanged);
|
||||
connect(tcsd, &TypeCodeSelectorDlg::finished,
|
||||
this, &TopicSelector::onSubDialogFinished);
|
||||
QString topicName(QString::fromStdString(topicDesc.name));
|
||||
tcsd->init(topicName, m_typeCodeInfos.value(topicName), topicDesc.type);
|
||||
tcsd->show();
|
||||
m_dialogs[topicDesc.name] = tcsd;
|
||||
}
|
||||
|
||||
void TopicSelector::onOk()
|
||||
{
|
||||
NGLSettings::instance()->setInfoValues(m_typeCodeInfos);
|
||||
emit typeCodeInfosChanged();
|
||||
for (TypeCodeSelectorDlg *tcsd : qAsConst(m_dialogs))
|
||||
tcsd->reject();
|
||||
accept();
|
||||
}
|
||||
|
||||
void TopicSelector::onCancel()
|
||||
{
|
||||
for (TypeCodeSelectorDlg *tcsd : qAsConst(m_dialogs))
|
||||
tcsd->reject();
|
||||
reject();
|
||||
}
|
||||
|
||||
void TopicSelector::onSubDialogFinished()
|
||||
{
|
||||
TypeCodeSelectorDlg *tcsd = qobject_cast<TypeCodeSelectorDlg *>(sender());
|
||||
if (!tcsd)
|
||||
return;
|
||||
|
||||
std::string key = m_dialogs.key(tcsd);
|
||||
m_dialogs.remove(key);
|
||||
}
|
||||
|
||||
void TopicSelector::onTypeCodeInfosChanged(const QString &typeCode, const QStringList &infoList)
|
||||
{
|
||||
m_typeCodeInfos[typeCode] = infoList;
|
||||
}
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* Select Topic data for editing/setting default display data
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nglwidgets/ngldialog.h>
|
||||
#include <QMap>
|
||||
#include <dds/dds.hpp>
|
||||
|
||||
class QTableWidgetItem;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class TopicSelector;
|
||||
}
|
||||
|
||||
class TypeCodeSelectorDlg;
|
||||
class TopicSortModel;
|
||||
class TreeModel;
|
||||
|
||||
class TopicSelector : public NGLDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Usage
|
||||
{
|
||||
SampleSkeletons,
|
||||
TypeCodeInfos
|
||||
};
|
||||
|
||||
explicit TopicSelector(Usage usage, QWidget *parent = nullptr);
|
||||
~TopicSelector();
|
||||
|
||||
private slots:
|
||||
void onAdd();
|
||||
void onEdit();
|
||||
void onOk();
|
||||
void onCancel();
|
||||
void onTypeCodeInfosChanged(const QString &typeCode, const QStringList &infoList);
|
||||
void onSubDialogFinished();
|
||||
signals:
|
||||
void data(const dds::core::xtypes::DynamicData &d, const QString &topicName);
|
||||
void typeCodeInfosChanged();
|
||||
private:
|
||||
QMap<QString, QStringList> m_typeCodeInfos;
|
||||
QMap<std::string, TypeCodeSelectorDlg *> m_dialogs;
|
||||
Ui::TopicSelector *m_ui;
|
||||
TopicSortModel *m_dataSortModel;
|
||||
TopicSortModel *m_functionSortModel;
|
||||
TreeModel *m_treeSrcModelData;
|
||||
TreeModel *m_treeSrcModelFunction;
|
||||
};
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#include "typecodeselector.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QCheckBox>
|
||||
#include <QGroupBox>
|
||||
#include <QScrollArea>
|
||||
|
||||
|
||||
TypeCodeSelector::TypeCodeSelector(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_logger(DdsView::getLogger("TypeCodeSelector"))
|
||||
{
|
||||
}
|
||||
|
||||
bool TypeCodeSelector::init(const QStringList &checkedValues, const dds::core::xtypes::DynamicType &type)
|
||||
{
|
||||
setWindowTitle(QString::fromStdString(type.name()));
|
||||
|
||||
// make dialog scrollable (for large topics (like ExplosionCluster)
|
||||
// is this really the easiest way?
|
||||
QScrollArea *scrollArea = new QScrollArea(this);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
|
||||
QGridLayout *gl = new QGridLayout(this);
|
||||
gl->addWidget(scrollArea);
|
||||
|
||||
QWidget *contentWidget = new QWidget(this);
|
||||
scrollArea->setWidget(contentWidget);
|
||||
|
||||
return generateUi(type, contentWidget, checkedValues);
|
||||
}
|
||||
|
||||
bool TypeCodeSelector::generateUi(const dds::core::xtypes::DynamicType &type, QWidget *parentWidget, const QStringList &checkedValues, const QString &ns)
|
||||
{
|
||||
if (type.kind() != dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
return false;
|
||||
|
||||
const dds::core::xtypes::StructType &structType = static_cast<const dds::core::xtypes::StructType &>(type);
|
||||
|
||||
QGridLayout *gl = new QGridLayout(parentWidget);
|
||||
|
||||
int row = 0;
|
||||
dds::core::xtypes::StructType typeCurrent = structType;
|
||||
bool hasParent = true;
|
||||
while (hasParent)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (const dds::core::xtypes::Member &member : typeCurrent.members())
|
||||
{
|
||||
const QString name = QString::fromStdString(member.name());
|
||||
|
||||
if (member.type().kind() == dds::core::xtypes::TypeKind::STRUCTURE_TYPE)
|
||||
{
|
||||
const dds::core::xtypes::DynamicType &childType = static_cast<const dds::core::xtypes::DynamicType &>(member.type());
|
||||
QGroupBox *gb = new QGroupBox(name, parentWidget);
|
||||
gl->addWidget(gb, row, 0, 1, 1);
|
||||
generateUi(childType, gb, checkedValues, ns + name + QLatin1Char('.'));
|
||||
}
|
||||
else
|
||||
{
|
||||
QCheckBox *cb = new QCheckBox(name, this);
|
||||
cb->setObjectName(ns + name);
|
||||
connect(cb, &QCheckBox::clicked, this, &TypeCodeSelector::onCbClicked);
|
||||
if (checkedValues.contains(ns + name, Qt::CaseInsensitive))
|
||||
cb->setChecked(true);
|
||||
gl->addWidget(cb, row, 0, 1, 1);
|
||||
}
|
||||
++row;
|
||||
}
|
||||
if (typeCurrent.has_parent())
|
||||
typeCurrent = typeCurrent.parent();
|
||||
else
|
||||
hasParent = false;
|
||||
}
|
||||
catch (const dds::core::Exception &)
|
||||
{
|
||||
// finished
|
||||
}
|
||||
}
|
||||
gl->addItem(new QSpacerItem(20, 4, QSizePolicy::Minimum, QSizePolicy::Expanding), row, 0, 1, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList TypeCodeSelector::getData() const
|
||||
{
|
||||
QStringList sl;
|
||||
const auto children = findChildren<QCheckBox *>();
|
||||
for (QCheckBox *cb : children)
|
||||
{
|
||||
if (cb->isChecked())
|
||||
sl << cb->objectName();
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
void TypeCodeSelector::onCbClicked(bool checked)
|
||||
{
|
||||
QCheckBox *cb = qobject_cast<QCheckBox *>(sender());
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
emit itemSelected(cb->objectName(), checked);
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <dds/dds.hpp>
|
||||
#include "logger.h"
|
||||
|
||||
class TypeCodeSelector : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TypeCodeSelector(QWidget *parent = nullptr);
|
||||
|
||||
bool init(const QStringList &checkedValues, const dds::core::xtypes::DynamicType &type);
|
||||
bool generateUi(const dds::core::xtypes::DynamicType &type, QWidget *parentWidget, const QStringList &checkedValues, const QString &ns = QString());
|
||||
QStringList getData() const;
|
||||
|
||||
signals:
|
||||
void itemSelected(const QString &name, bool selected);
|
||||
|
||||
private slots:
|
||||
void onCbClicked(bool checked);
|
||||
|
||||
private:
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#include "typecodeselectordlg.h"
|
||||
#include "ui_typecodeselectordlg.h"
|
||||
|
||||
#include "typecodeselector.h"
|
||||
|
||||
TypeCodeSelectorDlg::TypeCodeSelectorDlg(QWidget *parent)
|
||||
: NGLDialog(parent)
|
||||
, m_ui(new Ui::TypeCodeSelectorDlg)
|
||||
, m_logger(DdsView::getLogger("TypeCodeSelectorDlg"))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onOk, this, &TypeCodeSelectorDlg::onOk);
|
||||
connect(m_ui->nglDialogButtonBox, &NGLDialogButtonBox::onCancel, this, &TypeCodeSelectorDlg::reject);
|
||||
connect(m_ui->typeCodeSelector, &TypeCodeSelector::itemSelected, this, &TypeCodeSelectorDlg::onItemSelected);
|
||||
}
|
||||
|
||||
TypeCodeSelectorDlg::~TypeCodeSelectorDlg()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
bool TypeCodeSelectorDlg::init(const QString &typeCode, const QStringList &infoValues, const dds::core::xtypes::DynamicType &type)
|
||||
{
|
||||
bool retVal = m_ui->typeCodeSelector->init(infoValues, type);
|
||||
for (const QString &str : infoValues)
|
||||
onItemSelected(str, true);
|
||||
|
||||
setWindowTitle(typeCode);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void TypeCodeSelectorDlg::onOk()
|
||||
{
|
||||
QStringList sl;
|
||||
|
||||
sl.reserve(m_ui->lwTypeCodes->count());
|
||||
for (int i = 0; i < m_ui->lwTypeCodes->count(); ++i)
|
||||
sl << m_ui->lwTypeCodes->item(i)->text();
|
||||
|
||||
emit typeCodeInfosChanged(windowTitle(), sl);
|
||||
accept();
|
||||
}
|
||||
|
||||
void TypeCodeSelectorDlg::onItemSelected(const QString &name, bool selected)
|
||||
{
|
||||
if (selected)
|
||||
m_ui->lwTypeCodes->addItem(name);
|
||||
else
|
||||
{
|
||||
const auto items = m_ui->lwTypeCodes->findItems(name, Qt::MatchFixedString);
|
||||
for (QListWidgetItem *lwi : items)
|
||||
delete lwi;
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <nglwidgets/ngldialog.h>
|
||||
#include "typecodeselector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class TypeCodeSelectorDlg;
|
||||
}
|
||||
|
||||
class TypeCodeSelectorDlg : public NGLDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TypeCodeSelectorDlg(QWidget *parent = nullptr);
|
||||
~TypeCodeSelectorDlg();
|
||||
|
||||
bool init(const QString &typeCode, const QStringList &infoValues, const dds::core::xtypes::DynamicType &type);
|
||||
signals:
|
||||
void typeCodeInfosChanged(const QString &typeCode, const QStringList &infoList);
|
||||
|
||||
private slots:
|
||||
void onOk();
|
||||
void onItemSelected(const QString &name, bool selected);
|
||||
|
||||
private:
|
||||
Ui::TypeCodeSelectorDlg *m_ui;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CoordDetailDlg</class>
|
||||
<widget class="QDialog" name="CoordDetailDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>313</width>
|
||||
<height>97</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Enter coordinates</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="latLabel">
|
||||
<property name="text">
|
||||
<string>Lat:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="4">
|
||||
<widget class="LtsCoordWidget::LtsCoordWidget" name="positionEdit"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lonLabel">
|
||||
<property name="text">
|
||||
<string>Lon:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLabel" name="latText">
|
||||
<property name="text">
|
||||
<string notr="true">latText</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="lonText">
|
||||
<property name="text">
|
||||
<string notr="true">latText</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="4">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LtsCoordWidget::LtsCoordWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsCoordTrans/lts_coord_widget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DetailViewDlg</class>
|
||||
<widget class="NGLDialog" name="DetailViewDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>186</width>
|
||||
<height>116</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustIgnored</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="DetailView" name="detailView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>174</width>
|
||||
<height>88</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="NGLDialogButtonBox" name="nglDialogButtonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>NGLStandardButton::Cancel|NGLStandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NGLDialog</class>
|
||||
<extends>QDialog</extends>
|
||||
<header>nglwidgets/ngldialog.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NGLDialogButtonBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>nglwidgets/ngldialogbuttonbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>DetailView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>detailview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+251
@@ -0,0 +1,251 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1101</width>
|
||||
<height>642</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>485</width>
|
||||
<height>531</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Settings</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Exercise</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QRadioButton" name="rbPlayback">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Rep&lay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QTreeView" name="trvFunctionTopics">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QSpinBox" name="sbExercise">
|
||||
<property name="maximum">
|
||||
<number>999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Data Topics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbConnect">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbDisconnect">
|
||||
<property name="text">
|
||||
<string>Disconnect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Function Topics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Exercise Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QTreeView" name="trvDataTopics">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QRadioButton" name="rbExercise">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>E&xercise</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>485</width>
|
||||
<height>531</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Samples</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="SampleView" name="sampleView"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="cbRepeat">
|
||||
<property name="text">
|
||||
<string>Repeat</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QToolButton" name="pbPlay">
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../Z/src/nglwidgets/nglresources.qrc">
|
||||
<normaloff>:/icons/png/start_playback.png</normaloff>
|
||||
<normalon>:/icons/png/pause_playback.png</normalon>
|
||||
<disabledoff>:/icons/png/start_playback_disabled.png</disabledoff>
|
||||
<disabledon>:/icons/png/pause_playback_disabled.png</disabledon>:/icons/png/start_playback.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="TelegramView" name="telegramView"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QDockWidget" name="dwDetailViewTree">
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTreeView" name="trvTopicData">
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="animated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>SampleView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>sampleview.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TelegramView</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>telegramview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>sbExercise</tabstop>
|
||||
<tabstop>pbConnect</tabstop>
|
||||
<tabstop>pbDisconnect</tabstop>
|
||||
<tabstop>sampleView</tabstop>
|
||||
<tabstop>pbPlay</tabstop>
|
||||
<tabstop>cbRepeat</tabstop>
|
||||
<tabstop>telegramView</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../../Z/src/nglwidgets/nglresources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TopicSelector</class>
|
||||
<widget class="NGLDialog" name="TopicSelector">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>642</width>
|
||||
<height>465</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Select Topic...</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QTreeView" name="tvDataTopics">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>373</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DragOnly</enum>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Data Topics</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QTreeView" name="tvFunctionTopics">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>373</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DragOnly</enum>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Function Topics</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="NGLDialogButtonBox" name="nglDialogButtonBox" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NGLDialog</class>
|
||||
<extends>QDialog</extends>
|
||||
<header>nglwidgets/ngldialog.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NGLDialogButtonBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>nglwidgets/ngldialogbuttonbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TypeCodeSelectorDlg</class>
|
||||
<widget class="NGLDialog" name="TypeCodeSelectorDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>368</width>
|
||||
<height>514</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="TypeCodeSelector" name="typeCodeSelector" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QListWidget" name="lwTypeCodes">
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="NGLDialogButtonBox" name="nglDialogButtonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>NGLStandardButton::Cancel|NGLStandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NGLDialog</class>
|
||||
<extends>QDialog</extends>
|
||||
<header>nglwidgets/ngldialog.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NGLDialogButtonBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>nglwidgets/ngldialogbuttonbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TypeCodeSelector</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>typecodeselector.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VersionView</class>
|
||||
<widget class="NGLDialog" name="VersionView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>330</width>
|
||||
<height>299</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbitmap">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbVersion">
|
||||
<property name="text">
|
||||
<string>Version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="lblVersion">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbDate">
|
||||
<property name="text">
|
||||
<string>Date:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="lblDate">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="NGLDialogButtonBox" name="buttonBox" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NGLDialog</class>
|
||||
<extends>QDialog</extends>
|
||||
<header>nglwidgets/ngldialog.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>NGLDialogButtonBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>nglwidgets/ngldialogbuttonbox.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+512
@@ -0,0 +1,512 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* * XmlHelper - convert xml to a (dynamic) dds structure and save a dds structure as xml
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "xmlhelper.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <dds/core/xtypes/AliasType.hpp>
|
||||
#include "topic_tree_model.h"
|
||||
|
||||
namespace XmlHelper
|
||||
{
|
||||
|
||||
static void getPrimitiveData(const dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const std::string &memberName,
|
||||
QDomDocument &doc,
|
||||
QDomElement ¤tNode);
|
||||
static void getSequenceData(const dds::core::xtypes::DynamicData &data,
|
||||
QDomDocument &doc,
|
||||
QDomElement ¤tNode);
|
||||
static void getContainerData(const dds::core::xtypes::DynamicData &data,
|
||||
QDomDocument &doc,
|
||||
QDomElement ¤tNode);
|
||||
|
||||
static bool setPrimitiveData(dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QString &text);
|
||||
static void setContainerData(dds::core::xtypes::DynamicData &data,
|
||||
const QDomElement ¤tNode);
|
||||
|
||||
static void setSequenceData(dds::core::xtypes::DynamicData &data, const QDomElement ¤tNode);
|
||||
|
||||
void dds2Xml(const dds::core::xtypes::DynamicData &data, const QString &topicName, QDomDocument &doc, QDomElement ¤tNode)
|
||||
{
|
||||
QDomElement rootNode = doc.createElement(QStringLiteral("DdsData"));
|
||||
rootNode.setAttribute(QStringLiteral("count"), data.member_count());
|
||||
rootNode.setAttribute(QStringLiteral("type"), QString::fromStdString(data.type().name()));
|
||||
rootNode.setAttribute(QStringLiteral("topic"), topicName);
|
||||
currentNode.appendChild(rootNode);
|
||||
getContainerData(data, doc, rootNode);
|
||||
}
|
||||
|
||||
void getContainerData(const dds::core::xtypes::DynamicData &data, QDomDocument &doc, QDomElement ¤tNode)
|
||||
{
|
||||
const dds::core::xtypes::StructType &dtData = static_cast<const dds::core::xtypes::StructType &>(data.type());
|
||||
dds::core::xtypes::StructType dtDataCopy = dtData;
|
||||
|
||||
for (uint32_t member_index = 1; member_index <= data.member_count(); ++member_index)
|
||||
{
|
||||
const dds::core::xtypes::Member &child = TopicDataModel::memberFromType(dtData, dtDataCopy, member_index - 1);
|
||||
const dds::core::xtypes::TypeKind typeKind = child.type().kind();
|
||||
|
||||
try
|
||||
{
|
||||
if (typeKind == dds::core::xtypes::TypeKind::ALIAS_TYPE)
|
||||
{
|
||||
const dds::core::xtypes::DynamicData inner = data.value<dds::core::xtypes::DynamicData>(member_index);
|
||||
dds::core::xtypes::DynamicType innerType = inner.type();
|
||||
dds::core::xtypes::TypeKind innerTypeKind = innerType.kind();
|
||||
|
||||
while (innerTypeKind == dds::core::xtypes::TypeKind::ALIAS_TYPE)
|
||||
{
|
||||
dds::core::xtypes::AliasType alias = static_cast<dds::core::xtypes::AliasType &>(innerType);
|
||||
innerType = alias.related_type();
|
||||
innerTypeKind = innerType.kind();
|
||||
}
|
||||
|
||||
if ((typeKind.underlying() & dds::core::xtypes::TypeKind::PRIMITIVE_TYPE) == dds::core::xtypes::TypeKind::PRIMITIVE_TYPE ||
|
||||
(typeKind == dds::core::xtypes::TypeKind::STRING_TYPE) ||
|
||||
(typeKind == dds::core::xtypes::TypeKind::ENUMERATION_TYPE))
|
||||
getPrimitiveData(data, member_index, child.name(), doc, currentNode);
|
||||
else
|
||||
getContainerData(inner, doc, currentNode);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((typeKind.underlying() & dds::core::xtypes::TypeKind::PRIMITIVE_TYPE) == dds::core::xtypes::TypeKind::PRIMITIVE_TYPE ||
|
||||
(typeKind == dds::core::xtypes::TypeKind::STRING_TYPE) ||
|
||||
(typeKind == dds::core::xtypes::TypeKind::ENUMERATION_TYPE))
|
||||
{
|
||||
getPrimitiveData(data, member_index, child.name(), doc, currentNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeKind == dds::core::xtypes::TypeKind::SEQUENCE_TYPE ||
|
||||
typeKind == dds::core::xtypes::TypeKind::ARRAY_TYPE)
|
||||
{
|
||||
const dds::core::xtypes::DynamicData inner = data.value<dds::core::xtypes::DynamicData>(member_index);
|
||||
|
||||
QDomElement fieldNode = doc.createElement(QString::fromStdString(child.name()));
|
||||
fieldNode.setAttribute(QStringLiteral("count"), inner.member_count());
|
||||
currentNode.appendChild(fieldNode);
|
||||
if (inner.member_count() > 0)
|
||||
getSequenceData(inner, doc, fieldNode); // does not work
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((typeKind.underlying() & dds::core::xtypes::TypeKind::AGGREGATION_TYPE) == dds::core::xtypes::TypeKind::AGGREGATION_TYPE)
|
||||
{
|
||||
dds::core::xtypes::DynamicData inner = data.value<dds::core::xtypes::DynamicData>(member_index);
|
||||
|
||||
QDomElement fieldNode = doc.createElement(QString::fromStdString(child.name()));
|
||||
fieldNode.setAttribute(QStringLiteral("count"), inner.member_count());
|
||||
fieldNode.setAttribute(QStringLiteral("type"), QString::fromStdString(inner.type().name()));
|
||||
currentNode.appendChild(fieldNode);
|
||||
getContainerData(inner, doc, fieldNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
qWarning("unhandled parameter at idx: %d, name: %s, type: %x", member_index, child.name().c_str(), typeKind.underlying());
|
||||
|
||||
}
|
||||
catch (const dds::core::Exception &e)
|
||||
{
|
||||
qWarning("got exception while getting container data: %s", e.what());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getPrimitiveData(const dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const std::string &memberName,
|
||||
QDomDocument &doc,
|
||||
QDomElement ¤tNode)
|
||||
{
|
||||
QDomElement primNode = doc.createElement(QString::fromStdString(memberName));
|
||||
currentNode.appendChild(primNode);
|
||||
|
||||
QString retString;
|
||||
switch (data.member_info(member_index).member_kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
retString = QString::number(data.value<char>(member_index));
|
||||
break;
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
case dds::core::xtypes::TypeKind::CHAR_32_TYPE:
|
||||
#else
|
||||
case dds::core::xtypes::TypeKind::CHAR_16_TYPE:
|
||||
#endif
|
||||
retString = QString::number(data.value<DDS_Wchar>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
retString = QString::number(data.value<uint8_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
retString = QString::number(data.value<int16_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
retString = QString::number(data.value<uint16_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
retString = QString::number(data.value<int32_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
retString = QString::number(data.value<uint32_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
retString = QString::number(data.value<int64_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
retString = QString::number(data.value<uint64_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
retString = QString::number(data.value<float>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
retString = QString::number(data.value<double>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
retString = QString::number(data.value<bool>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
retString = QString::number(data.value<DDS_UnsignedLong>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
const dds::core::string tmp = data.value<dds::core::string>(member_index);
|
||||
if (tmp.c_str()) // unset -> nullptr
|
||||
retString = QString::fromUtf8(tmp.c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
retString = QStringLiteral("?-Unknown Primitive Type-?");
|
||||
break;
|
||||
}
|
||||
if (!retString.isEmpty())
|
||||
primNode.appendChild(doc.createTextNode(retString));
|
||||
}
|
||||
|
||||
void getPrimSeqData(const dds::core::xtypes::DynamicData &data, uint32_t member_index, QDomDocument &doc, QDomElement elemNode )
|
||||
{
|
||||
QString retString;
|
||||
|
||||
switch (data.member_info(member_index).member_kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
retString = QString::number(data.value<char>(member_index));
|
||||
break;
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
case dds::core::xtypes::TypeKind::CHAR_32_TYPE:
|
||||
#else
|
||||
case dds::core::xtypes::TypeKind::CHAR_16_TYPE:
|
||||
#endif
|
||||
retString = QString::number(data.value<DDS_Wchar>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
retString = QString::number(data.value<uint8_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
retString = QString::number(data.value<int16_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
retString = QString::number(data.value<uint16_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
retString = QString::number(data.value<int32_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
retString = QString::number(data.value<uint32_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
retString = QString::number(data.value<int64_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
retString = QString::number(data.value<uint64_t>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
retString = QString::number(data.value<float>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
retString = QString::number(data.value<double>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
retString = QString::number(data.value<bool>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
retString = QString::number(data.value<DDS_UnsignedLong>(member_index));
|
||||
break;
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
const dds::core::string tmp = data.value<dds::core::string>(member_index);
|
||||
if (tmp.c_str()) // unset -> nullptr
|
||||
retString = QString::fromUtf8(tmp.c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
retString = QStringLiteral("?-Unknown Primitive Type-?");
|
||||
break;
|
||||
}
|
||||
|
||||
elemNode.appendChild(doc.createTextNode(retString));
|
||||
}
|
||||
|
||||
void getSequenceData(const dds::core::xtypes::DynamicData &data, QDomDocument &doc, QDomElement ¤tNode)
|
||||
{
|
||||
for (uint32_t member_index = 1; member_index <= data.member_count(); ++member_index)
|
||||
{
|
||||
QDomElement elemNode = doc.createElement(currentNode.tagName() + QLatin1Char('.') + QString::number(member_index));
|
||||
currentNode.appendChild(elemNode);
|
||||
|
||||
const rti::core::xtypes::DynamicDataMemberInfo info = data.member_info(member_index);
|
||||
|
||||
switch (info.member_kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::STRUCTURE_TYPE:
|
||||
case dds::core::xtypes::TypeKind::SEQUENCE_TYPE:
|
||||
case dds::core::xtypes::TypeKind::ARRAY_TYPE:
|
||||
case dds::core::xtypes::TypeKind::ALIAS_TYPE:
|
||||
case dds::core::xtypes::TypeKind::AGGREGATION_TYPE:
|
||||
{
|
||||
const dds::core::xtypes::DynamicData inner = data.value<dds::core::xtypes::DynamicData>(member_index);
|
||||
getContainerData(inner, doc, elemNode);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
getPrimSeqData(data, member_index, doc, elemNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString topicNameFromXml(const QDomElement ¤tNode)
|
||||
{
|
||||
return currentNode.attribute(QStringLiteral("topic"));
|
||||
}
|
||||
|
||||
dds::core::xtypes::DynamicData xml2Dds(const QDomElement ¤tNode, const DDSGeneric::TopicDescription &td)
|
||||
{
|
||||
static const dds::core::xtypes::StructType s_unknown("Unknown");
|
||||
|
||||
QString typeName = currentNode.attribute(QStringLiteral("type"));
|
||||
|
||||
if (td.name.empty())
|
||||
return dds::core::xtypes::DynamicData(s_unknown);
|
||||
|
||||
const dds::core::xtypes::StructType &tc = typeName.compare(QString::fromStdString(td.typeName), Qt::CaseInsensitive) == 0 ? td.type : s_unknown;
|
||||
|
||||
dds::core::xtypes::DynamicData data(tc);
|
||||
setContainerData(data, currentNode);
|
||||
return data;
|
||||
}
|
||||
|
||||
void setContainerData(dds::core::xtypes::DynamicData &data, const QDomElement ¤tNode)
|
||||
{
|
||||
QDomNodeList dnl = currentNode.childNodes();
|
||||
for (int i = 0; i < dnl.size(); ++i)
|
||||
{
|
||||
const QDomNode dn = dnl.at(i);
|
||||
if (!dn.isElement())
|
||||
continue;
|
||||
|
||||
const QDomElement de = dn.toElement();
|
||||
try
|
||||
{
|
||||
rti::core::xtypes::DynamicDataMemberInfo info = data.member_info(de.nodeName().toStdString());
|
||||
const dds::core::xtypes::TypeKind typeKind = info.member_kind();
|
||||
|
||||
if ((typeKind.underlying() & dds::core::xtypes::TypeKind::AGGREGATION_TYPE) == dds::core::xtypes::TypeKind::AGGREGATION_TYPE)
|
||||
{
|
||||
rti::core::xtypes::LoanedDynamicData loanedData = data.loan_value(info.member_index());
|
||||
dds::core::xtypes::DynamicData &innerData = loanedData.get();
|
||||
setContainerData(innerData, de);
|
||||
}
|
||||
else if((typeKind.underlying() & dds::core::xtypes::TypeKind::SEQUENCE_TYPE) == dds::core::xtypes::TypeKind::SEQUENCE_TYPE)
|
||||
{
|
||||
|
||||
rti::core::xtypes::LoanedDynamicData loanedData = data.loan_value(info.member_index());
|
||||
dds::core::xtypes::DynamicData &innerData = loanedData.get();
|
||||
setSequenceData(innerData, de);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!setPrimitiveData(data, info.member_index(), de.text()))
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (dds::core::Exception &e)
|
||||
{
|
||||
qWarning("got exception while setting container data: %s", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void setSequenceData(dds::core::xtypes::DynamicData &data, const QDomElement ¤tNode)
|
||||
{
|
||||
QDomNodeList dnl = currentNode.childNodes();
|
||||
for (int i = 0; i < dnl.size(); ++i)
|
||||
{
|
||||
const QDomNode dn = dnl.at(i);
|
||||
if (!dn.isElement())
|
||||
continue;
|
||||
|
||||
const QDomElement de = dn.toElement();
|
||||
try
|
||||
{
|
||||
if(true)
|
||||
//if(dds::core::xtypes::is_primitive_type(data.type().kind().underlying())) // find out the type!
|
||||
{
|
||||
if (!setPrimitiveData(data, i+1, de.text()))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
rti::core::xtypes::LoanedDynamicData loanedData = data.loan_value(i+1);
|
||||
dds::core::xtypes::DynamicData &innerData = loanedData.get();
|
||||
setContainerData(innerData, de);
|
||||
}
|
||||
}
|
||||
catch (dds::core::Exception &e)
|
||||
{
|
||||
qWarning("got exception while setting container data: %s", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool setPrimitiveData(dds::core::xtypes::DynamicData &data,
|
||||
uint32_t member_index,
|
||||
const QString &text)
|
||||
{
|
||||
bool bOk;
|
||||
switch (data.member_info(member_index).member_kind().underlying())
|
||||
{
|
||||
case dds::core::xtypes::TypeKind::CHAR_8_TYPE:
|
||||
{
|
||||
char val = text.toUInt(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<char>(member_index, val);
|
||||
break;
|
||||
}
|
||||
#if RTI_DDS_VERSION_MAJOR < 6
|
||||
case dds::core::xtypes::TypeKind::CHAR_32_TYPE:
|
||||
#else
|
||||
case dds::core::xtypes::TypeKind::CHAR_16_TYPE:
|
||||
#endif
|
||||
{
|
||||
DDS_Wchar val = text.toUInt(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<DDS_Wchar>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::UINT_8_TYPE:
|
||||
{
|
||||
uint8_t val = text.toUInt(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<uint8_t>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::INT_16_TYPE:
|
||||
{
|
||||
int16_t val = text.toInt(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<int16_t>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::UINT_16_TYPE:
|
||||
{
|
||||
uint16_t val = text.toUInt(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<uint16_t>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::INT_32_TYPE:
|
||||
{
|
||||
int32_t val = text.toInt(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<int32_t>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::UINT_32_TYPE:
|
||||
{
|
||||
uint32_t val = text.toUInt(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<uint32_t>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::INT_64_TYPE:
|
||||
{
|
||||
int64_t val = text.toLongLong(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<int64_t>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::UINT_64_TYPE:
|
||||
{
|
||||
uint64_t val = text.toULongLong(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<uint64_t>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::FLOAT_32_TYPE:
|
||||
{
|
||||
float val = text.toFloat(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<float>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::FLOAT_64_TYPE:
|
||||
{
|
||||
double val = text.toDouble(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<double>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::BOOLEAN_TYPE:
|
||||
{
|
||||
bool val = text.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0 ? true : false;
|
||||
data.value<bool>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::ENUMERATION_TYPE:
|
||||
{
|
||||
DDS_UnsignedLong val = text.toULong(&bOk);
|
||||
if (!bOk)
|
||||
return false;
|
||||
data.value<DDS_UnsignedLong>(member_index, val);
|
||||
break;
|
||||
}
|
||||
case dds::core::xtypes::TypeKind::STRING_TYPE:
|
||||
{
|
||||
data.value<std::string>(member_index, text.toStdString());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace XmlHelper
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* * XmlHelper - convert xml to a (dynamic) dds structure and save a dds structure as xml
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dds/core/xtypes/DynamicData.hpp>
|
||||
#include <DDS/Generic/topic_description.h>
|
||||
#include <QString>
|
||||
|
||||
class QDomDocument;
|
||||
class QDomElement;
|
||||
|
||||
namespace XmlHelper
|
||||
{
|
||||
void dds2Xml(const dds::core::xtypes::DynamicData &data,
|
||||
const QString &topicName,
|
||||
QDomDocument &doc,
|
||||
QDomElement ¤tNode);
|
||||
QString topicNameFromXml(const QDomElement ¤tNode);
|
||||
dds::core::xtypes::DynamicData xml2Dds(const QDomElement ¤tNode,
|
||||
const DDSGeneric::TopicDescription &td);
|
||||
}
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Show the current version
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#include "zwmsui_version.h"
|
||||
|
||||
#include "ui_zwmsui_versionview.h"
|
||||
|
||||
#ifndef LTS_VERSION_MAJOR
|
||||
#define LTS_VERSION_MAJOR "undefined"
|
||||
#endif
|
||||
#ifndef LTS_VERSION_MINOR
|
||||
#define LTS_VERSION_MINOR "undefined"
|
||||
#endif
|
||||
#ifndef LTS_VERSION_PATCH
|
||||
#define LTS_VERSION_PATCH "undefined"
|
||||
#endif
|
||||
#ifndef LTS_VERSION_DATE
|
||||
#define LTS_VERSION_DATE "undefined"
|
||||
#endif
|
||||
#ifndef JENKINS_BUILD_NUMBER
|
||||
#define JENKINS_BUILD_NUMBER "unknown"
|
||||
#endif
|
||||
static const char ver_major[] = LTS_VERSION_MAJOR;
|
||||
static const char ver_minor[] = LTS_VERSION_MINOR;
|
||||
static const char ver_patch[] = LTS_VERSION_PATCH;
|
||||
static const char ver_date[] = LTS_VERSION_DATE;
|
||||
static const char ver_jenkins[] = JENKINS_BUILD_NUMBER;
|
||||
|
||||
VersionView::VersionView(NGL::Settings *settings, const QString &label, QWidget *parent)
|
||||
: NGLDialog(parent)
|
||||
, m_ui(new Ui::VersionView)
|
||||
, m_settings(settings)
|
||||
, m_label(label)
|
||||
, m_logger(DdsView::getLogger("VersionView"))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
QFont f = font();
|
||||
f.setBold(true);
|
||||
m_ui->lblVersion->setFont(f);
|
||||
m_ui->lblDate->setFont(f);
|
||||
fill();
|
||||
connect(m_ui->buttonBox, &NGLDialogButtonBox::onClose, this, &VersionView::accept);
|
||||
|
||||
}
|
||||
|
||||
VersionView::~VersionView()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void VersionView::fill()
|
||||
{
|
||||
const QString versionString = QStringLiteral("%1.%2-%3 (rev: %4)")
|
||||
.arg(QString::fromLatin1(ver_major),
|
||||
QString::fromLatin1(ver_minor),
|
||||
QString::fromLatin1(ver_jenkins),
|
||||
QString::fromLatin1(ver_patch));
|
||||
m_ui->lblVersion->setText(versionString);
|
||||
m_ui->lblDate->setText(QString::fromLatin1(ver_date));
|
||||
|
||||
resize(sizeHint());
|
||||
}
|
||||
|
||||
void VersionView::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QDialog::resizeEvent(event);
|
||||
|
||||
// Background Logo RDE
|
||||
QPixmap pm(m_settings->bitmapPath() + QLatin1String("logo_RDE.svg"));
|
||||
pm = pm.scaled(m_ui->lbitmap->width() * 0.9, m_ui->lbitmap->height() * 0.9, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
m_ui->lbitmap->setPixmap(pm);
|
||||
}
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Show the current version
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <nglwidgets/ngldialog.h>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include "logger.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class VersionView;
|
||||
}
|
||||
|
||||
class VersionView : public NGLDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
VersionView(NGL::Settings *settings, const QString &label, QWidget *parent = nullptr);
|
||||
~VersionView();
|
||||
|
||||
protected:
|
||||
void fill();
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
protected:
|
||||
Ui::VersionView *m_ui;
|
||||
NGL::Settings *m_settings;
|
||||
bool m_serverVersionShown;
|
||||
QString m_label;
|
||||
|
||||
private:
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
Reference in new issue
Block a user