951 lines
30 KiB
C++
Executable File
951 lines
30 KiB
C++
Executable File
/**
|
|
* @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();
|
|
}
|