465 lines
15 KiB
C++
Executable File
465 lines
15 KiB
C++
Executable File
/**
|
|
* @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;
|
|
}
|