#include "substream_json_filter.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QMimeData>
ADTF_PLUGIN(
"Qt5 Substream JSON Display Plugin (BETA)", cQtSubstreamDisplayJsonFilter)
namespace
{
template<typename T>
struct struct_initializer : public T
{
template<typename... Args>
struct_initializer(Args&&... args)
: T {std::forward<Args>(args)...}
{}
};
std::string substream_path_to_substream_name(const std::string& strPath)
{
auto lstPathTokens = QString::fromStdString(strPath).split(".", Qt::KeepEmptyParts);
if (lstPathTokens.size() >= 1)
{
lstPathTokens.removeFirst();
const auto strSubstreamName = lstPathTokens.join(".");
return strSubstreamName.toStdString();
}
return {};
}
}
cDroppableTextBrowser::cDroppableTextBrowser(const IDragDestination& oDragDestination) :
m_oDragDestination(oDragDestination)
{}
void cDroppableTextBrowser::dragEnterEvent(QDragEnterEvent* event)
{
const QMimeData* pMimeData = event->mimeData();
if (pMimeData->hasText())
{
const auto strJson = pMimeData->text();
const auto oJsonDocument = QJsonDocument::fromJson(strJson.toUtf8());
const auto strPath = oJsonDocument.object()["path"].toString();
const auto strSubstreamName = QString::fromStdString(substream_path_to_substream_name(strPath.toStdString()));
if (m_oDragDestination.AcceptsSubstreamName(strSubstreamName))
{
event->acceptProposedAction();
}
}
}
void cDroppableTextBrowser::dragMoveEvent(QDragMoveEvent* event)
{
event->acceptProposedAction();
}
void cDroppableTextBrowser::dropEvent(QDropEvent* event)
{
const QMimeData* pMimeData = event->mimeData();
if (pMimeData->hasText())
{
const auto strJson = pMimeData->text();
const auto oJsonDocument = QJsonDocument::fromJson(strJson.toUtf8());
const auto strPath = oJsonDocument.object()["path"].toString();
emit dropped(strPath);
}
}
cQtSubstreamDisplayJsonFilter::cQtSubstreamDisplayJsonFilter()
{
setObjectName("Substream Display Json Filter");
SetDescription("Use this filter to visualize the sample data of a received input which match the substream name/ID.");
set_help_link(*
this,
"$(ADTF_DIR)/doc/adtf_html/page_substream_json_display.html#sec_substream_json_display");
m_oDisplayServer = CreateInterfaceClient<adtf::substreamdisplay::IDisplayServer>("display_server");
set_description(*
this,
"display_server",
"display server object from Qt5 Substream Display.");
m_pReader = CreateInputPin("input", stream_meta_type_substreams());
}
tResult cQtSubstreamDisplayJsonFilter::Init(tInitStage eStage)
{
const auto fnGetAdtfMainWindow = []()
{
QWidget* pAdtfMainWindow = nullptr;
const auto lstTopLevelWidgets = qApp->topLevelWidgets();
for (const auto pTopLevelWidget : lstTopLevelWidgets)
{
const auto strClassName = QString(pTopLevelWidget->metaObject()->className());
if (strClassName == "cMainADTFWindow")
{
pAdtfMainWindow = pTopLevelWidget;
break;
}
}
return pAdtfMainWindow;
};
if (eStage == StagePostConnect)
{
if (m_oDisplayServer.IsValid())
{
const auto pDisplayHandler = m_oDisplayServer->GetServer()->RegisterDisplaySingleton(DISPLAY_TYPE, DISPLAY_BASE_NAME, {} );
connect(pDisplayHandler, &cDisplayHandler::showInDisplay, [this, &fnGetAdtfMainWindow](const QString& strPath)
{
auto strSubstreamName = ISubstreamItemData::GetPathComponents(strPath).strSubStreamName;
if (AcceptsSubstreamName(strSubstreamName))
{
SetFilteredSubstreamName(strSubstreamName.toStdString());
UpdateUi();
}
else
{
const auto pAdtfMainWindow = fnGetAdtfMainWindow();
QMessageBox::warning(pAdtfMainWindow,
tr("Showing substream"),
QString(tr("The substream / component '%1' cannot be shown in the display '%2'.")
.arg(strPath)
.arg(DISPLAY_BASE_NAME)));
}
});
QObject::connect(pDisplayHandler, &cDisplayHandler::closeDisplay, []()
{
});
QObject::connect(m_pSampleJsonTextBrowser, &cDroppableTextBrowser::dropped, [this](const QString& strPath)
{
const auto strName = substream_path_to_substream_name(strPath.toStdString());
SetFilteredSubstreamName(strName);
UpdateUi();
});
}
}
}
bool cQtSubstreamDisplayJsonFilter::AcceptsSubstreamName(const QString& strSubstreamName) const
{
const auto itSubstreamDef = std::find_if(std::begin(m_vecSubstreamDefs), std::end(m_vecSubstreamDefs),
[&strSubstreamName](const std::shared_ptr<cSubstreamDef>& pSubstreamDef)
{ return pSubstreamDef->m_strName == strSubstreamName.toStdString(); });
if (itSubstreamDef != std::end(m_vecSubstreamDefs))
{
const auto oSubstreamDef = *itSubstreamDef;
try
{
const cSampleCodecFactory oCodecFactory(oSubstreamDef->m_pStreamType);
if (
IS_OK(oCodecFactory.IsValid()))
{
return true;
}
}
catch (...)
{}
}
return false;
}
QWidget* cQtSubstreamDisplayJsonFilter::CreateView()
{
m_pMainWidget = std::make_shared<QWidget>();
const auto pMainLayout = new QVBoxLayout;
m_pMainWidget->setLayout(pMainLayout);
pMainLayout->setMargin(0);
pMainLayout->setSpacing(3);
const auto pHeadLayout = new QHBoxLayout;
pMainLayout->addLayout(pHeadLayout);
pHeadLayout->addWidget(new QLabel(tr("Substream Name:")));
m_pSubstreamNameLineEdit = new QLineEdit;
m_pSubstreamNameLineEdit->setObjectName("json_filter_substream_name_lineedit");
pHeadLayout->addWidget(m_pSubstreamNameLineEdit);
m_pSubstreamNameLineEdit->setSizePolicy(QSizePolicy::MinimumExpanding, m_pSubstreamNameLineEdit->sizePolicy().verticalPolicy());
m_pSubstreamNameLineEdit->setReadOnly(true);
m_pSubstreamNameLineEdit->setStyleSheet("QLineEdit { background-color: lightgray; }");
pMainLayout->addWidget(new QLabel(tr("Substream Content (JSON)")));
m_pSampleJsonTextBrowser = new cDroppableTextBrowser(*this);
m_pSampleJsonTextBrowser->setObjectName("json_filter_sample_json_text_browser");
pMainLayout->addWidget(m_pSampleJsonTextBrowser);
connect(this, &cQtSubstreamDisplayJsonFilter::newSubstreamSample, this,
[this](QString strSampleJson)
{ m_pSampleJsonTextBrowser->setText(strSampleJson); },
Qt::QueuedConnection);
return m_pMainWidget.get();
}
void cQtSubstreamDisplayJsonFilter::ReleaseView()
{
m_pMainWidget.reset();
}
tResult cQtSubstreamDisplayJsonFilter::OnTimer()
{
}
tResult cQtSubstreamDisplayJsonFilter::AcceptType(ISampleReader* pReader,
const iobject_ptr<const IStreamType>& pStreamType)
{
stream_meta_type_substreams::ListSubStreams(*pStreamType.Get(), [&](const char* strName, uint32_t nSubStreamId)
{
auto pSubStreamType = stream_meta_type_substreams::GetSubStreamType(*pStreamType.Get(), strName);
const auto pSubstreamDef = std::make_shared<struct_initializer<cSubstreamDef>>(nSubStreamId, strName, pSubStreamType);
m_vecSubstreamDefs.push_back(pSubstreamDef);
});
}
tResult cQtSubstreamDisplayJsonFilter::ProcessInput(ISampleReader* pReader,
const iobject_ptr<const ISample>& pSample)
{
Q_UNUSED(pReader);
std::lock_guard<std::recursive_mutex> oLock(m_oMutex);
const auto itSubstreamDef = std::find_if(std::begin(m_vecSubstreamDefs), std::end(m_vecSubstreamDefs),
[nSubstreamId](const std::shared_ptr<cSubstreamDef>& pSubstreamDef)
{ return pSubstreamDef->m_nId == nSubstreamId; });
if (itSubstreamDef != std::end(m_vecSubstreamDefs))
{
const auto pSubstreamDef = *itSubstreamDef;
if (m_strFilteredSubstreamName.has_value() && pSubstreamDef->m_strName == *m_strFilteredSubstreamName)
{
if (m_oFilteredCodecFactory.has_value() &&
IS_OK(m_oFilteredCodecFactory->IsValid()))
{
const auto oDecoder = m_oFilteredCodecFactory->MakeDecoderFor(pSample);
const auto strJson = DecodeToJson(oDecoder, pSample);
emit newSubstreamSample(QString::fromStdString(strJson));
}
else
{
emit newSubstreamSample({});
}
}
}
}
void cQtSubstreamDisplayJsonFilter::SetFilteredSubstreamName(const std::string& strFilteredSubstreamName)
{
m_strFilteredSubstreamName = strFilteredSubstreamName;
m_oFilteredCodecFactory.reset();
if (m_pSampleJsonTextBrowser != nullptr)
{
m_pSampleJsonTextBrowser->clear();
}
const auto itSubstreamDef = std::find_if(std::begin(m_vecSubstreamDefs), std::end(m_vecSubstreamDefs),
[&strFilteredSubstreamName](const std::shared_ptr<cSubstreamDef>& pSubstreamDef)
{ return pSubstreamDef->m_strName == strFilteredSubstreamName; });
if (itSubstreamDef != std::end(m_vecSubstreamDefs))
{
const auto oSubstreamDef = *itSubstreamDef;
try
{
const cSampleCodecFactory oCodecFactory(oSubstreamDef->m_pStreamType);
if (
IS_OK(oCodecFactory.IsValid()))
{
m_strFilteredSubstreamName = strFilteredSubstreamName;
m_oFilteredCodecFactory = std::move(oCodecFactory);
}
m_pReader->RequestSamples(m_pRequest, oSubstreamDef->m_nId);
}
catch(...)
{}
}
UpdateUi();
}
std::string cQtSubstreamDisplayJsonFilter::DecodeToJson(const cSampleDecoder& oDecoder, const iobject_ptr<const ISample>& pSample)
{
adtf::util::cString strJSONObject = adtf::util::cString::Format(
"{\n \"timestamp\": %" PRIi64
",\n \"data\":\n {", pSample->GetTime());
size_t nElementIndex = 0;
[&strJSONObject, &nElementIndex](const auto& oElement)
{
strJSONObject.Append(adtf::util::cString::Format("%s\n \"%s\": %s",
nElementIndex > 0 ? "," : "",
oElement.getFullName().c_str(),
oElement.getStringValue().c_str()));
++nElementIndex;
});
strJSONObject.Append("\n }\n}");
return strJSONObject.GetPtr();
}
void cQtSubstreamDisplayJsonFilter::UpdateUi()
{
m_pSubstreamNameLineEdit->clear();
if (m_strFilteredSubstreamName.has_value() && m_pSubstreamNameLineEdit != nullptr)
{
m_pSubstreamNameLineEdit->setText(QString::fromStdString(*m_strFilteredSubstreamName));
}
}
#define ADTF_PLUGIN(__plugin_identifier,...)
The ADTF Plugin Macro will add the code of a adtf::ucom::ant::IPlugin implementation.
#define RETURN_IF_FAILED(s)
Return if expression is failed, which requires the calling function's return type to be tResult.
#define RETURN_NOERROR
Return status ERR_NOERROR, which requires the calling function's return type to be tResult.
#define IS_OK(s)
Check if result is OK.
string_base< cStackString > cString
cString implementation for a stack string which works on stack if string is lower than A_UTILS_DEFAUL...
void set_description(base::ant::IConfiguration &oConfig, const char *strDescription)
Sets description information.
void set_help_link(base::ant::IConfiguration &oConfig, const char *strUrl)
Set the link to the corresponding help/documentation.
uint32_t get_sample_substream_id(const ant::ISample &oSample)
Copyright © Audi Electronics Venture GmbH.