Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+468
@@ -0,0 +1,468 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
*
|
||||
* MainWindow for DDS View
|
||||
*
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include "nglsettings.h"
|
||||
#include "zwmsui_version.h"
|
||||
#include "telegramview.h"
|
||||
#include "detailviewdlg.h"
|
||||
#include "topic_tree_model.h"
|
||||
#include "topicselector.h"
|
||||
#include "sampleview.h"
|
||||
|
||||
#include <DDS/Generic/topic_type_plugin.h>
|
||||
|
||||
#include <TopicDiscover/topic_discoverer.h>
|
||||
#include <LtsPluginManager/lts_plugin_loader.h>
|
||||
|
||||
#include <QMenuBar>
|
||||
|
||||
MainWindow::MainWindow(int domainId, int exerciseId, DDS::QosProfile::ConnectionType ct, const QUrl &tcpDiscoveryUrl, QWidget *parent)
|
||||
: NGLMainWindow(parent)
|
||||
, m_dsr(nullptr)
|
||||
, m_ddsCredentials()
|
||||
, m_topicDiscoverer(new TopicDiscoverer(domainId, this))
|
||||
, m_dataSortModel(new TopicSortModel(this))
|
||||
, m_functionSortModel(new TopicSortModel(this))
|
||||
, m_ui(new Ui::MainWindow)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_ui->sbExercise->setValue(exerciseId);
|
||||
|
||||
setWindowTitle(tr("DDS View - Domain %1").arg(domainId));
|
||||
m_ui->splitter->setSizes(QList<int>() << 550 << 750);
|
||||
|
||||
m_menu[QLatin1String("Edit")] = new QMenu(tr("Edit"));
|
||||
m_menuBar->insertMenu(m_menu[QLatin1String("Help")]->menuAction(), m_menu[QLatin1String("Edit")]);
|
||||
|
||||
m_menu[QLatin1String("View")] = new QMenu(tr("View"));
|
||||
m_menuBar->insertMenu(m_menu[QLatin1String("Help")]->menuAction(), m_menu[QLatin1String("View")]);
|
||||
|
||||
QAction *action;
|
||||
|
||||
action = m_ui->dwDetailViewTree->toggleViewAction();
|
||||
action->setText(tr("Toggle Details (tree)"));
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+t")));
|
||||
m_menu[QLatin1String("View")]->addAction(action);
|
||||
|
||||
action = new QAction(tr("Load Sample List"), this);
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+o")));
|
||||
connect(action, &QAction::triggered, m_ui->sampleView, &SampleView::onLoadSampleList);
|
||||
m_menu[QLatin1String("File")]->insertAction(m_fileActionBefore, action);
|
||||
|
||||
action = new QAction(tr("Save Sample List"), this);
|
||||
action->setShortcut(QKeySequence(tr("Ctrl+s")));
|
||||
connect(action, &QAction::triggered, m_ui->sampleView, &SampleView::onSaveSampleList);
|
||||
m_menu[QLatin1String("File")]->insertAction(m_fileActionBefore, action);
|
||||
|
||||
action = new QAction(tr("Configure Details"), this);
|
||||
connect(action, &QAction::triggered, this, &MainWindow::onConfigureDetails);
|
||||
m_menu[QLatin1String("Edit")]->addAction(action);
|
||||
|
||||
setVersionView();
|
||||
|
||||
m_treeSrcModelData = new TreeModel(m_ui->trvDataTopics);
|
||||
m_dataSortModel->setSourceModel(m_treeSrcModelData);
|
||||
m_ui->trvDataTopics->setModel(m_dataSortModel);
|
||||
m_dataSortModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
m_treeSrcModelFunction = new TreeModel(m_ui->trvFunctionTopics);
|
||||
m_functionSortModel->setSourceModel(m_treeSrcModelFunction);
|
||||
m_ui->trvFunctionTopics->setModel(m_functionSortModel);
|
||||
m_functionSortModel->sort(0, Qt::AscendingOrder);
|
||||
|
||||
m_topicDataModel = new TopicDataModel(m_ui->trvTopicData);
|
||||
m_ui->trvTopicData->setModel(m_topicDataModel);
|
||||
m_ui->trvTopicData->setColumnHidden(2, true);
|
||||
m_ui->trvTopicData->setColumnHidden(3, true);
|
||||
m_ui->trvTopicData->setColumnHidden(4, true);
|
||||
|
||||
connect(m_ui->trvDataTopics->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &MainWindow::onReaderChanged);
|
||||
connect(m_ui->trvFunctionTopics->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &MainWindow::onSelectionFunctionChanged);
|
||||
|
||||
connect(m_ui->sbExercise, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::onReaderChanged);
|
||||
|
||||
connect(m_ui->pbConnect, &QPushButton::clicked, this, &MainWindow::onConnect);
|
||||
connect(m_ui->pbDisconnect, &QPushButton::clicked, this, &MainWindow::onDisconnect);
|
||||
|
||||
connect(m_ui->pbPlay, &QPushButton::clicked, m_ui->sampleView, &SampleView::onPlayChecked);
|
||||
connect(m_ui->cbRepeat, &QCheckBox::clicked, m_ui->sampleView, &SampleView::onSetRepeat);
|
||||
connect(m_ui->sampleView, &SampleView::playerActive, m_ui->pbPlay, &QToolButton::setChecked);
|
||||
|
||||
m_ui->pbDisconnect->setEnabled(false);
|
||||
m_ddsCredentials.domain = -1;
|
||||
|
||||
connect(m_topicDiscoverer, &TopicDiscoverer::topicDescription,
|
||||
this, &MainWindow::onTopicDescription);
|
||||
|
||||
m_topicDiscoverer->init(ct, tcpDiscoveryUrl);
|
||||
|
||||
connect(m_ui->telegramView, &TelegramView::dataClicked,
|
||||
m_topicDataModel, &TopicDataModel::setTopicData);
|
||||
connect(m_ui->telegramView, &TelegramView::clearDetailView,
|
||||
m_topicDataModel, &TopicDataModel::reset);
|
||||
connect(m_ui->telegramView, &TelegramView::send,
|
||||
this, &MainWindow::onSend);
|
||||
connect(m_ui->sampleView, &SampleView::send,
|
||||
this, &MainWindow::onSend);
|
||||
|
||||
loadPlugins();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
m_versionView->disconnect(this);
|
||||
delete m_ui;
|
||||
delete m_dsr;
|
||||
}
|
||||
|
||||
IDL::PartitionType MainWindow::partitionType() const
|
||||
{
|
||||
if (m_ui->rbExercise->isChecked())
|
||||
return IDL::PartitionType::Exercise;
|
||||
|
||||
if (m_ui->rbPlayback->isChecked())
|
||||
return IDL::PartitionType::Replay;
|
||||
|
||||
// Maybe we add all the other options later...
|
||||
// default:
|
||||
return IDL::PartitionType::Exercise;
|
||||
}
|
||||
|
||||
MainWindow::DdsCredentials MainWindow::onReaderChanged()
|
||||
{
|
||||
const QModelIndexList milDt = m_ui->trvDataTopics->selectionModel()->selectedRows();
|
||||
const QModelIndexList milFt = m_ui->trvFunctionTopics->selectionModel()->selectedRows();
|
||||
|
||||
QVector<DDSGeneric::TopicDescription> vTopicDescription;
|
||||
vTopicDescription.reserve(vTopicDescription.size() + milFt.size());
|
||||
|
||||
for (const QModelIndex &idx : milDt)
|
||||
{
|
||||
const DDSGeneric::TopicDescription topicDesc = m_dataSortModel->getData(idx);
|
||||
if (topicDesc.typeName.empty())
|
||||
continue;
|
||||
DEBUG_LOG(m_logger) << "selected data topic: " << topicDesc.name;
|
||||
vTopicDescription << topicDesc;
|
||||
}
|
||||
for (const QModelIndex &idx : milFt)
|
||||
{
|
||||
const DDSGeneric::TopicDescription topicDesc = m_functionSortModel->getData(idx);
|
||||
if (topicDesc.typeName.empty())
|
||||
continue;
|
||||
DEBUG_LOG(m_logger) << "selected function topic: " << topicDesc.name;
|
||||
vTopicDescription << topicDesc;
|
||||
}
|
||||
|
||||
DdsCredentials current(m_ui->sbExercise->value(), m_topicDiscoverer->getDomainParticipant().domain_id(), partitionType(), vTopicDescription);
|
||||
updateButtons(current);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
class ScopedBoolean
|
||||
{
|
||||
public:
|
||||
explicit ScopedBoolean(bool &b)
|
||||
: m_b(b)
|
||||
{
|
||||
m_b = true;
|
||||
}
|
||||
~ScopedBoolean()
|
||||
{
|
||||
m_b = false;
|
||||
}
|
||||
private:
|
||||
bool &m_b;
|
||||
};
|
||||
|
||||
void MainWindow::onSelectionFunctionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||
{
|
||||
static bool bInReaderChanged = false;
|
||||
if (bInReaderChanged)
|
||||
return;
|
||||
ScopedBoolean b(bInReaderChanged);
|
||||
|
||||
static QItemSelection itemSelection;
|
||||
QModelIndexList mil;
|
||||
if (!selected.isEmpty())
|
||||
mil = selected.indexes();
|
||||
else if (!deselected.isEmpty())
|
||||
mil = deselected.indexes();
|
||||
else
|
||||
return; //should never happen
|
||||
|
||||
if (mil.isEmpty())
|
||||
return;
|
||||
const QModelIndex index = mil.first();
|
||||
|
||||
if (index.column() != 0)
|
||||
return;
|
||||
|
||||
const DDSGeneric::TopicDescription topicDesc = m_functionSortModel->getData(index);
|
||||
if (topicDesc.typeName.empty()) // --> knot de(selected)
|
||||
{
|
||||
int childCount = m_functionSortModel->rowCount(index);
|
||||
if (childCount == 0)
|
||||
return;
|
||||
|
||||
const QModelIndex startindex = m_functionSortModel->index(0, index.column(), index);
|
||||
const QModelIndex endindex = m_functionSortModel->index(childCount - 1, index.column(), index);
|
||||
itemSelection.merge(QItemSelection(startindex, endindex), QItemSelectionModel::Select);
|
||||
}
|
||||
else
|
||||
{
|
||||
// one child (de)selected
|
||||
if (!selected.isEmpty())
|
||||
{
|
||||
bool allSelected = true;
|
||||
int childs = m_functionSortModel->rowCount(index.parent());
|
||||
|
||||
for (int count = 0; count < childs; count ++)
|
||||
{
|
||||
QModelIndex sibling = index.sibling(count, index.column());
|
||||
if (sibling == index)
|
||||
continue; // ignore same ModelIndex
|
||||
if (!m_ui->trvFunctionTopics->selectionModel()->isSelected(sibling))
|
||||
{
|
||||
allSelected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allSelected)
|
||||
itemSelection.merge(QItemSelection(index.parent(), index.parent()), QItemSelectionModel::Select);
|
||||
itemSelection.append(selected);
|
||||
}
|
||||
else if (!deselected.isEmpty())
|
||||
{
|
||||
itemSelection.merge(QItemSelection(index.parent(), index.parent()), QItemSelectionModel::Select);
|
||||
itemSelection.append(deselected);
|
||||
}
|
||||
else
|
||||
return; //should never happen
|
||||
}
|
||||
|
||||
if (!selected.isEmpty() && !itemSelection.isEmpty())
|
||||
{
|
||||
m_ui->trvFunctionTopics->selectionModel()->select(itemSelection, QItemSelectionModel::Select);
|
||||
m_ui->trvFunctionTopics->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
|
||||
}
|
||||
else if (!deselected.isEmpty())
|
||||
{
|
||||
m_ui->trvFunctionTopics->selectionModel()->select(itemSelection, QItemSelectionModel::Deselect);
|
||||
m_ui->trvFunctionTopics->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Deselect);
|
||||
}
|
||||
else
|
||||
return; //should never happen
|
||||
|
||||
itemSelection.clear();
|
||||
|
||||
ERROR_LOG(m_logger) << "cur: " << m_ui->trvFunctionTopics->selectionModel()->selectedIndexes().size();
|
||||
|
||||
onReaderChanged();
|
||||
}
|
||||
|
||||
void MainWindow::updateButtons(const MainWindow::DdsCredentials ¤t)
|
||||
{
|
||||
if (current == m_ddsCredentials || m_dsr == nullptr)
|
||||
{
|
||||
m_ui->pbConnect->setText(tr("Connect"));
|
||||
m_ui->pbConnect->setEnabled(m_dsr == nullptr);
|
||||
m_ui->pbDisconnect->setEnabled(m_dsr != nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->pbConnect->setText(tr("Switch"));
|
||||
m_ui->pbConnect->setEnabled(true);
|
||||
m_ui->pbDisconnect->setEnabled(true);
|
||||
}
|
||||
m_ui->telegramView->setConnected(m_dsr != nullptr);
|
||||
m_ui->sampleView->setConnected(m_dsr != nullptr);
|
||||
}
|
||||
|
||||
void MainWindow::onSend(const QSharedPointer<dds::core::xtypes::DynamicData> &data, const QString &topicName)
|
||||
{
|
||||
if (!m_dsr || !data)
|
||||
return;
|
||||
|
||||
dds::core::xtypes::DynamicData dataToSend(*data);
|
||||
if (dataToSend.member_exists("sampleTime"))
|
||||
{
|
||||
qint64 curValue = dataToSend.value<qint64>("sampleTime");
|
||||
if (curValue <= 1000000)
|
||||
dataToSend.value("sampleTime", QDateTime::currentMSecsSinceEpoch() + curValue);
|
||||
}
|
||||
m_dsr->publish(dataToSend, topicName.toStdString());
|
||||
}
|
||||
|
||||
void MainWindow::onConnect()
|
||||
{
|
||||
if (m_ui->trvDataTopics->selectionModel()->selectedRows().isEmpty() &&
|
||||
m_ui->trvFunctionTopics->selectionModel()->selectedRows().isEmpty())
|
||||
return;
|
||||
|
||||
// Todo: Change of exercise not regarded!
|
||||
DdsCredentials cred = onReaderChanged();
|
||||
|
||||
if (!m_dsr)
|
||||
m_dsr = new DdsView::Initialisation(m_topicDiscoverer->getDomainParticipant(),
|
||||
cred.domain,
|
||||
cred.partitionType,
|
||||
cred.exercise,
|
||||
m_ui->telegramView->telegramModel());
|
||||
|
||||
// Set DroTypes after Initialisation
|
||||
m_dsr->initDDTCs(cred.droTypes);
|
||||
|
||||
m_ddsCredentials = cred;
|
||||
updateButtons(cred);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::onDisconnect()
|
||||
{
|
||||
delete m_dsr;
|
||||
m_dsr = nullptr;
|
||||
onReaderChanged();
|
||||
}
|
||||
|
||||
void MainWindow::onConfigureDetails()
|
||||
{
|
||||
TopicSelector *ts = new TopicSelector(TopicSelector::Usage::TypeCodeInfos, this);
|
||||
connect(ts, &TopicSelector::typeCodeInfosChanged, m_ui->telegramView, &TelegramView::onTypeCodeInfosChanged);
|
||||
ts->show();
|
||||
}
|
||||
|
||||
void MainWindow::onTopicDescription(const std::string &topicName, const std::string &typeName, const dds::core::xtypes::StructType &type)
|
||||
{
|
||||
if (NGLSettings::instance()->hasDataTopic(topicName) ||
|
||||
NGLSettings::instance()->hasFunctionTopic(topicName))
|
||||
return;
|
||||
|
||||
TopicDescriptionEx topicDesc;
|
||||
topicDesc.name = topicName;
|
||||
topicDesc.typeName = typeName;
|
||||
topicDesc.type = type;
|
||||
topicDesc.serviceName = tr("DDS");
|
||||
|
||||
TreeItem *parent = nullptr;
|
||||
|
||||
DEBUG_LOG(m_logger) << "TypeName:" << typeName;
|
||||
|
||||
|
||||
if (QString::fromStdString(typeName).endsWith(QLatin1String("Request")) ||
|
||||
QString::fromStdString(typeName).endsWith(QLatin1String("Reply")))
|
||||
{
|
||||
// FunctionTopic
|
||||
m_functionSortModel->addData(topicDesc, parent);
|
||||
NGLSettings::instance()->insertFunctionTopic(topicDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// DataTopic
|
||||
m_dataSortModel->addData(topicDesc, parent);
|
||||
NGLSettings::instance()->insertDataTopic(topicDesc);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setVersionView()
|
||||
{
|
||||
m_versionView = new VersionView(NGLSettings::instance(), tr("DDS View"), this);
|
||||
connect(m_versionView, &VersionView::destroyed,
|
||||
this, &MainWindow::onVersionViewDeleted, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
bool MainWindow::loadPlugin(const LtsPluginManager::PluginInstance &pi)
|
||||
{
|
||||
DDSGeneric::TopicTypePlugin *typePlugin = qobject_cast<DDSGeneric::TopicTypePlugin *>(pi.plugin);
|
||||
if (!typePlugin)
|
||||
return false;
|
||||
|
||||
const QMap<std::string, DDSGeneric::TopicDescription> dataTopics = typePlugin->getDataTopics();
|
||||
for (const DDSGeneric::TopicDescription &td : dataTopics)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Loading data topic from service: " << typePlugin->getServiceShortName()
|
||||
<< ", topic: " << td.name;
|
||||
TopicDescriptionEx tdEx(td);
|
||||
tdEx.serviceName = typePlugin->getServiceShortName();
|
||||
NGLSettings::instance()->insertDataTopic(tdEx);
|
||||
|
||||
// nullptr -> no parent -> model must search correct parent on first level
|
||||
m_dataSortModel->addData(tdEx, nullptr);
|
||||
}
|
||||
|
||||
|
||||
struct RequestReplyStruct
|
||||
{
|
||||
DDSGeneric::TopicDescription ftRequest;
|
||||
DDSGeneric::TopicDescription ftReply;
|
||||
|
||||
bool hasRequestAndReply() const { return !ftRequest.isEmpty() && !ftReply.isEmpty(); }
|
||||
};
|
||||
|
||||
QMap<std::string, RequestReplyStruct> tempData;
|
||||
const QMap<std::string, DDSGeneric::TopicDescription> functionTopics = typePlugin->getFunctionTopics();
|
||||
for (const DDSGeneric::TopicDescription &td : functionTopics)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "req: " << typePlugin->getServiceShortName() << " - " << td.name;
|
||||
const QString topicName = QString::fromStdString(td.name);
|
||||
if (topicName.endsWith(QLatin1String("Reply")))
|
||||
{
|
||||
const QString key = topicName.left(topicName.size() - QLatin1String("Reply").size());
|
||||
tempData[key.toStdString()].ftReply = td;
|
||||
}
|
||||
else if (topicName.endsWith(QLatin1String("Request")))
|
||||
{
|
||||
const QString key = topicName.left(topicName.size() - QLatin1String("Request").size());
|
||||
tempData[key.toStdString()].ftRequest = td;
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Got unknown FTC/S topic: " << td.name;
|
||||
// should not happend at all - print warning
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = tempData.cbegin(); it != tempData.cend(); ++it)
|
||||
{
|
||||
const std::string &key = it.key();
|
||||
const RequestReplyStruct &rr = it.value();
|
||||
|
||||
TopicDescriptionEx tdKnot;
|
||||
tdKnot.serviceName = typePlugin->getServiceShortName();
|
||||
tdKnot.name = key;
|
||||
|
||||
// nullptr -> no parent -> model must search correct parent on first level
|
||||
TreeItem *knot = m_functionSortModel->addData(tdKnot, nullptr);
|
||||
|
||||
TopicDescriptionEx tdReply(rr.ftReply);
|
||||
tdReply.serviceName = typePlugin->getServiceShortName();
|
||||
m_functionSortModel->addData(tdReply, knot);
|
||||
NGLSettings::instance()->insertFunctionTopic(tdReply);
|
||||
|
||||
TopicDescriptionEx tdRequest(rr.ftRequest);
|
||||
tdRequest.serviceName = typePlugin->getServiceShortName();
|
||||
m_functionSortModel->addData(tdRequest, knot);
|
||||
NGLSettings::instance()->insertFunctionTopic(tdRequest);
|
||||
}
|
||||
|
||||
return true; // do not unload plugin because we have some static QString references in there!
|
||||
}
|
||||
|
||||
void MainWindow::loadPlugins()
|
||||
{
|
||||
LtsPluginManager::LtsPluginLoader::getInstance()->loadPlugins(LtsPluginManager::PluginType::TOPIC_TYPE,
|
||||
[&](const LtsPluginManager::PluginInstance & pi) { return loadPlugin(pi); });
|
||||
}
|
||||
Reference in new issue
Block a user