Initial commit

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

No files matched your search

+91
View File
@@ -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")
+69
View File
@@ -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);
}
+36
View File
@@ -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;
};
+784
View File
@@ -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();
}
}
+103
View File
@@ -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;
};
+101
View File
@@ -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();
}
+51
View File
@@ -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;
};
+58
View File
@@ -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
+40
View File
@@ -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;
};
}
+102
View File
@@ -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:
+37
View File
@@ -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
+22
View File
@@ -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
+107
View File
@@ -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;
}
+468
View File
@@ -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 &current)
{
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); });
}
+102
View File
@@ -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 &current);
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);
};
+136
View File
@@ -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);
}
+59
View File
@@ -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()); }
};
+357
View File
@@ -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;
}
+202
View File
@@ -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;
};
+534
View File
@@ -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;
}
+114
View File
@@ -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;
};
+354
View File
@@ -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);
}
+81
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+79
View File
@@ -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
+45
View File
@@ -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;
};
}
+464
View File
@@ -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;
}
+106
View File
@@ -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;
};
+201
View File
@@ -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();
}
+57
View File
@@ -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();
};
+950
View File
@@ -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();
}
+172
View File
@@ -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;
};
+161
View File
@@ -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;
}
+60
View File
@@ -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;
};
+105
View File
@@ -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);
}
+26
View File
@@ -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;
};
+55
View File
@@ -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;
}
}
+30
View File
@@ -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;
};
+79
View File
@@ -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>
+92
View File
@@ -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>
+251
View File
@@ -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&amp;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&amp;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>
+83
View File
@@ -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>
+74
View File
@@ -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>
+98
View File
@@ -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>
+512
View File
@@ -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 &currentNode);
static void getSequenceData(const dds::core::xtypes::DynamicData &data,
QDomDocument &doc,
QDomElement &currentNode);
static void getContainerData(const dds::core::xtypes::DynamicData &data,
QDomDocument &doc,
QDomElement &currentNode);
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 &currentNode);
static void setSequenceData(dds::core::xtypes::DynamicData &data, const QDomElement &currentNode);
void dds2Xml(const dds::core::xtypes::DynamicData &data, const QString &topicName, QDomDocument &doc, QDomElement &currentNode)
{
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 &currentNode)
{
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 &currentNode)
{
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 &currentNode)
{
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 &currentNode)
{
return currentNode.attribute(QStringLiteral("topic"));
}
dds::core::xtypes::DynamicData xml2Dds(const QDomElement &currentNode, 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 &currentNode)
{
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 &currentNode)
{
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
+28
View File
@@ -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 &currentNode);
QString topicNameFromXml(const QDomElement &currentNode);
dds::core::xtypes::DynamicData xml2Dds(const QDomElement &currentNode,
const DDSGeneric::TopicDescription &td);
}
+77
View File
@@ -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);
}
+40
View File
@@ -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;
};