Remove modules not compiled by this branch's release build

Lts/Common: drop the 17 submodules excluded from the RELEASE_BUILD
branch of Lts/Common/CMakeLists.txt (CodeGen, LtsAppManager, LtsBotan,
LtsCoordinate, LtsElevation, LtsLogin, LtsMultiMonitor,
LtsPluginManager, LtsPrinting, LtsProjectionWorkstation,
LtsScriptTimeline, LtsSession, LtsTimeline, LtsTimelineClassic,
LtsVirtualKeyboard, LtsWidgets, TopicEditorBase). Only the 13
subdirectories actually built remain.

TrainingAreaBase: drop sources of targets excluded by
IF(NOT RELEASE_BUILD) / IF(BUILD_RTI) (svcTabClient, svcTabServer,
svcTrainingAreaBase exe, RecordReplayPlugin customization) and the 7
plugin directories not added in the release path (AdlerJson,
ApacheHlaJson, DebugJsonDir, HlaJson, Nmea, UAS, WlsTetra). Only
tabTelegrams, svcTabCommon and the TetraJson plugin are compiled.

Cross-checked: no kept compiled source includes headers from the
removed modules (GuiPluginInterface - the only LtsWidgets consumer -
is itself NOT RELEASE_BUILD-guarded).
This commit is contained in:
berrux-dev committed 2026-10-13 19:19:55 +02:00
1 parent eb4d067365
commit 855b0a6101
765 files changed
-128732

No files matched your search

@@ -1,173 +0,0 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Make ${Qt6_Widgets} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets)
set(Qt6_Widgets ${Qt6_Widgets})
else()
set(Qt6_Widgets "")
endif()
FIND_PACKAGE(RTI)
# KTextTemplate is Qt6-compatible replacement for Grantlee5
IF(BUILD_GRANTLEE)
FIND_PACKAGE(KF6TextTemplate REQUIRED)
ENDIF()
NGL_ADD_LIBRARY2(ALL codegen_base SHARED
codegen_export.h
SqlGenerator.cpp
SqlGenerator.h
StructBuilder.cpp
StructBuilder.h
XmlReader.cpp
XmlReader.h
)
NGL_TARGET_LINK_LIBRARIES(codegen_base
PRIVATE
Qt6::Xml
NGLLogger
ngltools
PUBLIC
Qt6::Core
)
NGL_TARGET_INCLUDE_DIRECTORIES(codegen_base
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}
)
# If RTI target is available, propagate its include dirs/compile defs
if (TARGET RTI::nddscpp2)
NGL_TARGET_INCLUDE_DIRECTORIES(codegen_base
PUBLIC
$<TARGET_PROPERTY:RTI::nddscpp2,INTERFACE_INCLUDE_DIRECTORIES>
)
NGL_TARGET_COMPILE_DEFINITIONS(codegen_base
PUBLIC
$<TARGET_PROPERTY:RTI::nddscpp2,INTERFACE_COMPILE_DEFINITIONS>
RTI_DDS_AVAILABLE
)
message(STATUS "RTI found; building codegen_base with RTI support")
else()
message(STATUS "RTI not found; building codegen_base without RTI support")
endif()
NGL_INSTALL_TARGET(codegen_base)
# KTextTemplate is Qt6-compatible replacement for Grantlee5
IF(BUILD_GRANTLEE AND TARGET KF6::TextTemplate)
NGL_ADD_LIBRARY(ALL codegen_grantlee STATIC
CppTopicGeneratorGrantlee.cpp
CppTopicGeneratorGrantlee.h
DaoGeneratorGrantlee.cpp
DaoGeneratorGrantlee.h
DatabaseGeneratorGrantlee.cpp
DatabaseGeneratorGrantlee.h
GeneratorGrantleeBase.cpp
GeneratorGrantleeBase.h
GrantleeWrappers.cpp
GrantleeWrappers.h
svc.idl.grantlee
svc_enums.idl.grantlee
svc_translation.cpp.grantlee
svc_translation.h.grantlee
svc_dao.cpp.grantlee
svc_dao.h.grantlee
svc_db.h.grantlee
svc_db_impl.h.grantlee
svc_db_impl.cpp.grantlee
svc_dtc.h.grantlee
svc_dtc_impl.cpp.grantlee
svc_dtc_impl.h.grantlee
svc_dts.h.grantlee
svc_dts_impl.cpp.grantlee
svc_dts_impl.h.grantlee
svc_ftc.h.grantlee
svc_ftc_impl.cpp.grantlee
svc_ftc_impl.h.grantlee
svc_fts.h.grantlee
svc_fts_impl.cpp.grantlee
svc_fts_impl.h.grantlee
svc_json.cpp.grantlee
svc_json.h.grantlee
svc_topic_type_plugin.cpp.grantlee
svc_topic_type_plugin.h.grantlee
svc_record_replay_plugin.cpp.grantlee
svc_record_replay_plugin.h.grantlee
codegen_simple.py
)
NGL_TARGET_LINK_LIBRARIES(codegen_grantlee
PRIVATE
NGLLogger
ProjectConfig
PUBLIC
codegen_base
KF6::TextTemplate
)
NGL_TARGET_INCLUDE_DIRECTORIES(codegen_grantlee
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_BINARY_DIR}/Z/src/NGLLogger
)
# Smoke-test: parse all .grantlee templates in this directory to detect parse/render regressions
find_package(Qt6 COMPONENTS Core QUIET)
if (Qt6_FOUND)
set(QTCORE_TARGET Qt6::Core)
else()
find_package(Qt6 COMPONENTS Core REQUIRED)
set(QTCORE_TARGET Qt6::Core)
endif()
add_executable(grantlee_parser_smoke tests/GrantleeParserSmoke.cpp)
target_link_libraries(grantlee_parser_smoke PRIVATE KF6::TextTemplate ${QTCORE_TARGET})
add_test(NAME GrantleeParserSmoke COMMAND grantlee_parser_smoke ${CMAKE_CURRENT_SOURCE_DIR})
NGL_ADD_EXECUTABLE(ALL ngl_codegen
main_grantlee.cpp
)
NGL_TARGET_LINK_LIBRARIES(ngl_codegen
codegen_grantlee
LtsCommandLineParser
nglcoreapplication
Qt6::Core
KF6::TextTemplate
)
NGL_WRAP_UI(codegen_gui_uics
main_grantlee_gui.ui
)
NGL_ADD_EXECUTABLE(WS AAR SV NO_TRANSLATION codegen_gui
${codegen_gui_uics}
main_grantlee_gui.cpp
main_grantlee_gui.ui
)
if(QT6_GUI_AVAILABLE)
NGL_TARGET_LINK_LIBRARIES(codegen_gui
codegen_grantlee
LtsCommandLineParser
nglapplication
${Qt6_Widgets}
KF6::TextTemplate
)
endif()
ELSE()
message(STATUS "BUILD_GRANTLEE disabled or Grantlee not found; skipping codegen_grantlee, ngl_codegen, and codegen_gui targets")
ENDIF()
@@ -1,330 +0,0 @@
#include "CppTopicGeneratorGrantlee.h"
#include "StructBuilder.h"
#include <NGLLogger/INGLLogger.h>
#include <QtCore/QCryptographicHash>
#include <ktexttemplate/context.h>
#ifdef RTI_HEADER_AVAILABLE
#include <dds/core/xtypes/EnumType.hpp>
#endif
using namespace CodeGen;
CppTopicGeneratorGrantlee::CppTopicGeneratorGrantlee() : GeneratorGrantleeBase("CodeGen.CppTopicGeneratorGrantlee") {}
bool CppTopicGeneratorGrantlee::init(const XmlReader* reader, StructBuilder* structBuilder)
{
m_reader = reader;
if (!GeneratorGrantleeBase::initInternal(reader, structBuilder))
return false;
addCustomFunctions(reader->getCustomFunctions());
return initDataTopics() && initFunctionTopics() && initIdlStructs();
}
bool CppTopicGeneratorGrantlee::initDataTopics()
{
DataTopicsWrapper* dataTopicsWrapper = new DataTopicsWrapper(QString(), this);
const auto dataTopics = m_reader->getIdlStructInformation();
QVector<IdlData> keyColumns;
QVector<IdlData> trColumns;
for (const CodeGen::TopicTypeInformation& tti : dataTopics)
{
if (!tti.isDataObject)
continue;
keyColumns.clear();
trColumns.clear();
const CompleteTopicTypeInformation ctti = m_structBuilder->topicTypeInformationByStructType(tti.name);
if (!ctti.isValid())
{
ERROR_LOG(m_logger) << "could not find informations for struct " << tti.name;
continue;
}
DataTopicWrapper* dtw = dataTopicsWrapper->addDataTopic(ctti);
getSpecialColumns(ctti, keyColumns, trColumns);
if (keyColumns.size() == 1)
dtw->setKeyColumnData(keyColumns.first());
dtw->setTrColumns(trColumns);
if (ctti.recordTopic)
{
const CompleteTableInformation cti(m_structBuilder->getRecordingTopicTable(ctti.name));
if (!cti.isEmpty())
dtw->setRecordTableInformation(cti);
}
else
{
if (ctti.isDataObject)
m_structBuilder->buildJSonStructureInformationFromIdlData(ctti);
}
}
m_ctx->insert(QStringLiteral("dataTopics"), dataTopicsWrapper);
QSet<QString> exportedSubTopics;
DataTopicsWrapper* subTopicsWrapper = new DataTopicsWrapper(QString(), this);
const QVector<QSharedPointer<DataTypeInfo>> serializeTopics = m_structBuilder->getSerializeFromToJsonTopics();
for (const QSharedPointer<DataTypeInfo>& dti : serializeTopics)
{
if (exportedSubTopics.contains(dti->idlType()))
continue;
if (dti->isStructure())
{
const CompleteTopicTypeInformation ctti = m_structBuilder->topicTypeInformationByStructType(dti->idlType());
if (!ctti.isValid())
{
ERROR_LOG(m_logger) << "could not find informations for struct " << dti->idlType();
continue;
}
const CompleteTableInformation cti(m_structBuilder->getRecordingTopicTable(ctti.name));
if (cti.isEmpty())
{
ERROR_LOG(m_logger) << "could not find informations for struct " << dti->idlType();
continue;
}
DEBUG_LOG(m_logger) << "Adding substruct topic " << ctti.name;
DataTopicWrapper* dtw = subTopicsWrapper->addDataTopic(ctti);
dtw->setRecordTableInformation(cti);
exportedSubTopics += dti->idlType();
}
}
m_ctx->insert(QStringLiteral("serializeTopics"), subTopicsWrapper);
QVector<MemberInformationListWrapper*> jsonTopics;
const QMap<QString, QVector<MemberInformation>> jsonExport = m_structBuilder->jsonStructureInformation();
for (auto it = jsonExport.begin(); it != jsonExport.end(); ++it)
{
MemberInformationListWrapper* mil = new MemberInformationListWrapper(it.key(), it.value(), this);
jsonTopics += mil;
}
m_ctx->insert(QStringLiteral("jsonTopics"), QVariant::fromValue(jsonTopics));
return true;
}
bool CppTopicGeneratorGrantlee::getSpecialColumns(const CompleteTopicTypeInformation& tti, QVector<IdlData>& keyColumns,
QVector<IdlData>& trColumns) const
{
DEBUG_LOG(m_logger) << "Get special columns for " << tti.name << " - base is " << tti.baseValueType;
if (!tti.isValid())
{
ERROR_LOG(m_logger) << "tti is not valid ...";
return false;
}
for (const IdlData& ci : tti.members)
{
if (ci.isKey)
keyColumns += ci;
if (ci.isTranslatable)
trColumns += ci;
}
if (tti.baseValueType.isEmpty())
return true;
return getSpecialColumns(topicTypeInformationByDerivedTopic(tti), keyColumns, trColumns);
}
bool CppTopicGeneratorGrantlee::initFunctionTopics()
{
const QVector<FunctionTopic> functionTopics = m_reader->getFunctionTopics();
FunctionTopicsWrapper* functionTopicsWrapper(new FunctionTopicsWrapper(QStringLiteral("functionTopics"), this));
for (const FunctionTopic& ft : functionTopics)
{
if (!handleStruct(ft, functionTopicsWrapper))
return false;
}
m_ctx->insert(QStringLiteral("functionTopics"), functionTopicsWrapper);
return true;
}
bool CppTopicGeneratorGrantlee::handleStruct(const FunctionTopic& ft, FunctionTopicsWrapper* functionTopicsWrapper)
{
DEBUG_LOG(m_logger) << "handling FunctionTopic: " << ft.name;
const CompleteTopicTypeInformation siRequest =
m_structBuilder->topicTypeInformationByStructType(ft.requestTopicType);
if (!siRequest.isValid())
{
WARNING_LOG(m_logger) << "Could not get topic informations about request topic " << ft.requestTopicName;
return false;
}
const CompleteTopicTypeInformation siReply = m_structBuilder->topicTypeInformationByStructType(ft.replyTopicType);
if (!siReply.isValid())
{
WARNING_LOG(m_logger) << "Could not get topic informations about reply topic " << ft.replyTopicType;
return false;
}
FunctionTopicWrapper* ftw = functionTopicsWrapper->addFunctionTopic(ft, siRequest, siReply);
const QByteArray hash =
QCryptographicHash::hash(ft.requestTopicName.toUtf8(), QCryptographicHash::Sha3_512).toHex().left(16);
ftw->setReplyHandlerHash(QStringLiteral("0x") + QString::fromLatin1(hash) + QStringLiteral("LL"));
if (!addRequesterRequestParams(ft.name, siRequest, ftw) || !addRequesterReplyParams(ft.name, siReply, ftw))
return false;
return true;
}
bool CppTopicGeneratorGrantlee::addRequesterRequestParams(const QString& request_name,
const CompleteTopicTypeInformation& request,
FunctionTopicWrapper* ftw)
{
if (request.hasBaseValue())
{
const CompleteTopicTypeInformation siBase =
m_structBuilder->topicTypeInformationByStructType(request.baseValueType);
if (!siBase.isValid())
{
WARNING_LOG(m_logger) << "Could not get topic informations about base request topic "
<< request.baseValueType;
return false;
}
if (!addRequesterRequestParams(request_name, siBase, ftw))
return false;
}
ftw->addRequestParams(request.members);
return true;
}
bool CppTopicGeneratorGrantlee::addRequesterReplyParams(const QString& request_name,
const CompleteTopicTypeInformation& reply,
FunctionTopicWrapper* ftw)
{
if (reply.hasBaseValue())
{
const CompleteTopicTypeInformation siBase =
m_structBuilder->topicTypeInformationByStructType(reply.baseValueType);
if (!siBase.isValid())
{
WARNING_LOG(m_logger) << "Could not get topic informations about base reply topic " << reply.baseValueType;
return false;
}
if (!addRequesterReplyParams(request_name, siBase, ftw))
return false;
}
ftw->addReplyParams(reply.members);
return true;
}
bool CppTopicGeneratorGrantlee::handleIdlStruct(const CompleteTopicTypeInformation& csi, QSet<QString>& idlStructNames,
QVector<IdlStructWrapper*>& idlStructs)
{
if (!csi.isValid())
return true;
if (idlStructNames.contains(csi.name))
return true;
if (csi.hasBaseValue())
{
const CompleteTopicTypeInformation ctti =
m_structBuilder->topicTypeInformationByStructType(csi.baseValueType, false);
if (ctti.isValid())
{
#ifdef RTI_HEADER_AVAILABLE
if (ctti.extensibility == dds::core::xtypes::ExtensibilityKind_def::FINAL)
{
ERROR_LOG(m_logger) << "Base struct (" << ctti.name
<< ") has final extensibility - this is not allowed";
return false;
}
#else
// RTI not available - skip extensibility check (assume EXTENSIBLE)
Q_UNUSED(ctti.extensibility);
#endif
if (!idlStructNames.contains(csi.baseValueType))
{
if (!handleIdlStruct(ctti, idlStructNames, idlStructs))
return false;
}
}
else
{
const int idx = csi.baseValueType.indexOf(QLatin1String("::"));
if (idx < 0)
{
ERROR_LOG(m_logger) << "idlStruct '" << csi.baseValueType << "' not found in idl-file";
return false;
}
}
}
for (const IdlData& idlData : csi.members)
{
QString idlType;
DEBUG_LOG(m_logger) << "member: " << idlData.type << " - " << idlData.name << " "
<< idlData.dataTypeInfo->isStructure() << " " << idlData.dataTypeInfo->isSequence();
if (idlData.dataTypeInfo->isStructure())
idlType = idlData.dataTypeInfo->idlType();
else if (idlData.dataTypeInfo->isSequence())
{
if (idlData.dataTypeInfo->innerDataType()->isStructure())
idlType = idlData.dataTypeInfo->innerDataType()->idlType();
}
if (idlType.isEmpty())
continue;
if (!handleIdlStruct(m_structBuilder->topicTypeInformationByStructType(idlType, false), idlStructNames,
idlStructs))
return false;
}
DEBUG_LOG(m_logger) << "Current structName: " << csi.name;
idlStructs += new IdlStructWrapper(csi, this);
idlStructNames.insert(csi.name);
return true;
}
bool CppTopicGeneratorGrantlee::initIdlStructs()
{
const QVector<CompleteTopicTypeInformation> csiData = m_structBuilder->topicTypeInformations();
QSet<QString> idlStructNames;
QVector<IdlStructWrapper*> idlStructs;
idlStructs.reserve(csiData.size());
for (const CompleteTopicTypeInformation& csi : csiData)
{
if (!handleIdlStruct(csi, idlStructNames, idlStructs))
return false;
}
m_ctx->insert(QStringLiteral("idl_structs"), QVariant::fromValue(idlStructs));
const QVector<EnumInformation> enumerations = m_structBuilder->enumInformations();
QVector<IdlEnumWrapper*> idlEnumerations;
idlEnumerations.reserve(enumerations.size());
for (const EnumInformation& ei : enumerations)
idlEnumerations += new IdlEnumWrapper(ei, this);
m_ctx->insert(QStringLiteral("idl_enums"), QVariant::fromValue(idlEnumerations));
exportUsedServices(m_reader);
return true;
}
CompleteTopicTypeInformation
CppTopicGeneratorGrantlee::topicTypeInformationByDerivedTopic(const CompleteTopicTypeInformation& tti) const
{
if (tti.baseValueType.isEmpty())
return CompleteTopicTypeInformation();
QString baseValueType = tti.baseValueType;
if (tti.name.contains(QLatin1String("::")) && !tti.baseValueType.contains(QLatin1String("::")))
{
const QStringList sl = tti.name.split(QStringLiteral("::"));
baseValueType = sl.at(0) + QLatin1String("::") + tti.baseValueType;
}
DEBUG_LOG(m_logger) << "topicTypeInformationByStructName(" << baseValueType << ")";
return m_structBuilder->topicTypeInformationByStructType(baseValueType);
}
@@ -1,41 +0,0 @@
#pragma once
#include "GeneratorGrantleeBase.h"
#include "GrantleeWrappers.h"
#include "StructBuilder.h"
#include <QtCore/QObject>
namespace CodeGen
{
class CppTopicGeneratorGrantlee final : public GeneratorGrantleeBase
{
Q_OBJECT
public:
explicit CppTopicGeneratorGrantlee();
bool init(const XmlReader* reader, StructBuilder* structBuilder) override;
private:
bool initDataTopics();
bool getSpecialColumns(const CompleteTopicTypeInformation& tti, QVector<IdlData>& keyColumns,
QVector<IdlData>& trColumns) const;
bool initFunctionTopics();
bool handleStruct(const FunctionTopic& ft, FunctionTopicsWrapper* functionTopicsWrapper);
bool addRequesterRequestParams(const QString& request_name, const CompleteTopicTypeInformation& request,
FunctionTopicWrapper* ftw);
bool addRequesterReplyParams(const QString& request_name, const CompleteTopicTypeInformation& reply,
FunctionTopicWrapper* ftw);
bool handleIdlStruct(const CompleteTopicTypeInformation& csi, QSet<QString>& idlStructNames,
QVector<IdlStructWrapper*>& idlStructs);
bool initIdlStructs();
CompleteTopicTypeInformation topicTypeInformationByDerivedTopic(const CompleteTopicTypeInformation& tti) const;
const XmlReader* m_reader = nullptr;
};
} // namespace CodeGen
@@ -1,62 +0,0 @@
/**
* @RDE_HEADER@
* Generate cpp structs from xml
* $Id: DaoGeneratorGrantlee.cpp 50446 2020-03-18 10:29:57Z ehrlicher $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/DaoGeneratorGrantlee.cpp $
* $Author: ehrlicher $
*/
#include "DaoGeneratorGrantlee.h"
#include "StructBuilder.h"
#include <NGLLogger/INGLLogger.h>
#include <grantlee/context.h>
using namespace CodeGen;
DaoGeneratorGrantlee::DaoGeneratorGrantlee()
: GeneratorGrantleeBase("CodeGen.DaoGeneratorGrantlee")
{}
bool DaoGeneratorGrantlee::init(const XmlReader *reader,
StructBuilder *structBuilder)
{
if (!GeneratorGrantleeBase::initInternal(reader, structBuilder))
return false;
// Is Service exercise-independent?
if (reader->isServiceExerciseIndependent())
m_ctx->insert(QStringLiteral("isExerciseIndependent"), QLatin1String("1"));
bool bHasDbNotifications = false;
QVector<DbStructWrapper *> vlServiceStructs;
QVector<DbStructWrapper *> vlStaticDataStructs;
const QVector<CompleteStructInformation> csiData = structBuilder->structInformations();
vlServiceStructs.reserve(csiData.size());
vlStaticDataStructs.reserve(csiData.size());
for (const CompleteStructInformation &csi : csiData)
{
DbStructWrapper *dbStructWrapper(new DbStructWrapper(csi, this));
if (csi.hasStaticContent)
vlStaticDataStructs += dbStructWrapper;
else
vlServiceStructs += dbStructWrapper;
if (!csi.dbNotifications.isEmpty())
bHasDbNotifications = true;
}
if (structBuilder->needPoint3d())
m_ctx->insert(QStringLiteral("need_point3d"), QLatin1String("1"));
if (structBuilder->needPgArrayInclude())
m_ctx->insert(QStringLiteral("need_pgarray"), QLatin1String("1"));
m_ctx->insert(QStringLiteral("cpp_structs"), QVariant::fromValue(vlServiceStructs));
m_ctx->insert(QStringLiteral("data_structs"), QVariant::fromValue(vlStaticDataStructs));
if (bHasDbNotifications)
m_ctx->insert(QStringLiteral("has_db_notifications"), QLatin1String("1"));
m_ctx->insert(QStringLiteral("library_target"), QStringLiteral("COMMON"));
exportUsedServices(reader);
return true;
}
@@ -1,30 +0,0 @@
/**
* @RDE_HEADER@
* Generate cpp structs from xml
* $Id: DaoGeneratorGrantlee.h 50446 2020-03-18 10:29:57Z ehrlicher $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/DaoGeneratorGrantlee.h $
* $Author: ehrlicher $
*/
#pragma once
#include "GeneratorGrantleeBase.h"
#include "StructBuilder.h"
#include "GrantleeWrappers.h"
#include "codegen_export.h"
namespace CodeGen
{
struct CompleteStructInformation;
class StructBuilder;
class XmlReader;
class DaoGeneratorGrantlee final : public GeneratorGrantleeBase
{
Q_OBJECT
public:
explicit DaoGeneratorGrantlee();
bool init(const XmlReader *reader, StructBuilder *structBuilder) override;
};
} // namespace CodeGen
@@ -1,59 +0,0 @@
#include "DatabaseGeneratorGrantlee.h"
#include "StructBuilder.h"
#include <NGLLogger/INGLLogger.h>
#include <ktexttemplate/context.h>
using namespace CodeGen;
DatabaseGeneratorGrantlee::DatabaseGeneratorGrantlee(bool targetIsServer)
: GeneratorGrantleeBase("CodeGen.CppStructGeneratorGrantlee"), m_targetIsServer(targetIsServer)
{
}
bool DatabaseGeneratorGrantlee::init(const XmlReader* reader, StructBuilder* structBuilder)
{
if (!GeneratorGrantleeBase::initInternal(reader, structBuilder))
return false;
if (reader->isServiceExerciseIndependent())
m_ctx->insert(QStringLiteral("isExerciseIndependent"), QLatin1String("1"));
bool bHasDbNotifications = false;
QVector<DbStructWrapper*> vlServiceStructs;
QVector<DbStructWrapper*> vlStaticDataStructs;
const QVector<CompleteStructInformation> csiData = structBuilder->structInformations();
vlServiceStructs.reserve(csiData.size());
vlStaticDataStructs.reserve(csiData.size());
for (const CompleteStructInformation& csi : csiData)
{
DbStructWrapper* dbStructWrapper(new DbStructWrapper(csi, this));
if (csi.hasStaticContent)
vlStaticDataStructs += dbStructWrapper;
else
vlServiceStructs += dbStructWrapper;
if (!csi.dbNotifications.isEmpty())
bHasDbNotifications = true;
}
addCustomFunctions(reader->getCustomFunctions());
if (structBuilder->needPoint3d())
m_ctx->insert(QStringLiteral("need_point3d"), QLatin1String("1"));
if (structBuilder->needPgArrayInclude())
m_ctx->insert(QStringLiteral("need_pgarray"), QLatin1String("1"));
m_ctx->insert(QStringLiteral("cpp_structs"), QVariant::fromValue(vlServiceStructs));
m_ctx->insert(QStringLiteral("data_structs"), QVariant::fromValue(vlStaticDataStructs));
if (bHasDbNotifications)
m_ctx->insert(QStringLiteral("has_db_notifications"), QLatin1String("1"));
m_ctx->insert(QStringLiteral("library_target"),
m_targetIsServer ? QStringLiteral("SERVER") : QStringLiteral("COMMON"));
exportUsedServices(reader);
return true;
}
@@ -1,27 +0,0 @@
#pragma once
#include "GeneratorGrantleeBase.h"
#include "GrantleeWrappers.h"
#include "StructBuilder.h"
#include <QtCore/QObject>
namespace CodeGen
{
struct CompleteStructInformation;
class StructBuilder;
class XmlReader;
class DatabaseGeneratorGrantlee final : public GeneratorGrantleeBase
{
Q_OBJECT
public:
explicit DatabaseGeneratorGrantlee(bool targetIsServer);
bool init(const XmlReader* reader, StructBuilder* structBuilder) override;
private:
bool m_targetIsServer;
};
} // namespace CodeGen
@@ -1,200 +0,0 @@
#include "GeneratorGrantleeBase.h"
#include "GrantleeWrappers.h"
#include "XmlReader.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtCore/QFile>
#include <QtCore/QRegularExpression>
using namespace CodeGen;
GeneratorGrantleeBase::GeneratorGrantleeBase(const char* loggerName)
: m_engine(new KTextTemplate::Engine)
, m_ctx(new KTextTemplate::Context)
, m_structBuilder(nullptr)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName))
{
// Add KTextTemplate plugin path for loading default tags/filters
// KTextTemplate plugins are in /usr/lib/x86_64-linux-gnu/qt6/plugins/kf6/ktexttemplate
// QT_PLUGIN_PATH should point to the parent directory containing the category folders
m_engine->addPluginPath(QStringLiteral("/usr/lib/x86_64-linux-gnu/qt6/plugins"));
}
GeneratorGrantleeBase::~GeneratorGrantleeBase()
{
delete m_engine;
delete m_ctx;
}
bool GeneratorGrantleeBase::initInternal(const XmlReader* reader, StructBuilder* structBuilder)
{
m_structBuilder = structBuilder;
if (reader)
{
m_ctx->insert(QStringLiteral("namespace"), reader->getServiceNamespace());
m_ctx->insert(QStringLiteral("namespace_abbr"), reader->getServiceAbbr().toLower());
m_ctx->insert(QStringLiteral("namespace_abbr_upper"), reader->getServiceAbbr().toUpper());
if (reader->getServiceNamespace() == QLatin1String("Core"))
m_ctx->insert(QStringLiteral("include_prefix"),
QStringLiteral("DDS/%1").arg(reader->getServiceNamespace()));
else
m_ctx->insert(QStringLiteral("include_prefix"), reader->getServiceNamespace());
m_ctx->insert(QStringLiteral("custom_record_replay"), reader->hasCustomRecordReplayPlugin());
}
return true;
}
bool GeneratorGrantleeBase::setTemplate(const QString& templateName, const QString& tmpl)
{
m_template = m_engine->newTemplate(tmpl, QStringLiteral("myTemplate"));
if (m_template->error())
{
WARNING_LOG(m_logger) << "Error parsing template '" << templateName << "': " << m_template->errorString();
return false;
}
return true;
}
QString GeneratorGrantleeBase::render() const
{
QString out = m_template->render(m_ctx);
const QRegularExpression rx(QLatin1String(";\\n([ ]+\\n)*"));
out = out.replace(rx, QStringLiteral(";\n"));
return out;
}
QString GeneratorGrantleeBase::errorString() const
{
return m_template->errorString();
}
bool GeneratorGrantleeBase::generateData(const QString& templateName, const QString& outDirectory)
{
QString content;
if (!readFile(templateName, content))
return false;
if (!setTemplate(templateName, content))
return false;
content = render();
QString outFilename;
if (!getFilenameFromData(content, outFilename))
return false;
QFile f(outDirectory + QLatin1Char('/') + outFilename);
if (!f.open(QIODevice::WriteOnly))
{
ERROR_LOG(m_logger) << "Error opening file '" << f.fileName() << "' for writing: " << f.errorString() << "("
<< f.error() << ")";
return false;
}
const QByteArray outData = content.toUtf8();
f.write(outData);
f.close();
DEBUG_LOG(m_logger) << "Written data to " << f.fileName();
return true;
}
bool GeneratorGrantleeBase::readFile(const QString& filename, QString& data) const
{
QFile f(filename);
if (!f.open(QIODevice::ReadOnly))
{
ERROR_LOG(m_logger) << "Error opening file " << filename << " - error: " << f.errorString();
return false;
}
data = QString::fromUtf8(f.readAll());
f.close();
return true;
}
bool GeneratorGrantleeBase::getFilenameFromData(const QString& dataToParse, QString& filename) const
{
const QString filenameMarker(QStringLiteral("// filename:"));
int idx = dataToParse.indexOf(filenameMarker);
if (idx < 0)
{
ERROR_LOG(m_logger) << "Could not find marker '" << filenameMarker << "' in data!";
return false;
}
idx += filenameMarker.length() + 1;
int endIdx = dataToParse.indexOf(QLatin1Char('\n'), idx);
if (endIdx < 0)
{
ERROR_LOG(m_logger) << "Could not find end marker '\\n' in data!";
return false;
}
filename = dataToParse.mid(idx, endIdx - idx).trimmed();
return true;
}
void GeneratorGrantleeBase::addCustomFunctions(const QVector<CustomServiceFunctionInformation>& customFunctions)
{
QVariantList vlCustomDbFunctions;
QVariantList vlCustomDtcFunctions;
vlCustomDbFunctions.reserve(customFunctions.size());
vlCustomDtcFunctions.reserve(customFunctions.size());
for (const CustomServiceFunctionInformation& csfi : customFunctions)
{
const QString signature(csfi.returnValue + QLatin1Char(' ') + csfi.methodName + QLatin1Char('(') +
csfi.signature + QLatin1Char(')') +
(csfi.isConst ? QStringLiteral(" const") : QString()));
CustomFunctionWrapper* customFunctionWrapper(new CustomFunctionWrapper(signature, this));
switch (csfi.type)
{
case CustomServiceFunctionInformation::Type::Db:
vlCustomDbFunctions += QVariant::fromValue(customFunctionWrapper);
break;
case CustomServiceFunctionInformation::Type::Dtc:
vlCustomDtcFunctions += QVariant::fromValue(customFunctionWrapper);
break;
case CustomServiceFunctionInformation::Type::DbNotify:
WARNING_LOG(m_logger) << "Internal error - Trying to handle DbNotify in addCustomFunctions(): "
<< csfi.methodName;
delete customFunctionWrapper;
break;
}
DEBUG_LOG(m_logger) << "Added custom db function " << csfi.methodName;
}
m_ctx->insert(QStringLiteral("custom_dtc_procs"), vlCustomDtcFunctions);
m_ctx->insert(QStringLiteral("custom_db_procs"), vlCustomDbFunctions);
}
void GeneratorGrantleeBase::exportUsedServices(const XmlReader* reader)
{
IdlServicesWrapper* idlServicesWrapper(new IdlServicesWrapper(QString(), this));
const QMap<QString, bool> usedServices = reader->usedServices();
for (auto it = usedServices.begin(); it != usedServices.end(); ++it)
{
if (!it.value())
continue;
const QString& svc = it.key();
const QString fullPath = reader->idlFileForService(svc);
const int idx = fullPath.lastIndexOf(QLatin1Char('.'));
if (idx < 0)
{
ERROR_LOG(m_logger) << "Could not parse file path: " << fullPath;
continue;
}
const QString idlFile = fullPath.left(idx);
if (idlFile.isEmpty())
{
WARNING_LOG(m_logger) << "Could not find idl file for service " << svc;
continue;
}
idlServicesWrapper->addService(svc, idlFile);
DEBUG_LOG(m_logger) << "Added service " << svc;
}
m_ctx->insert(QStringLiteral("ext_services"), idlServicesWrapper);
}
@@ -1,53 +0,0 @@
#ifndef GENERATORGRANTLEEBASE_H
#define GENERATORGRANTLEEBASE_H
#include "XmlReader.h"
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <ktexttemplate/template.h>
#include <ktexttemplate/context.h>
#include <ktexttemplate/engine.h>
namespace NGL
{
class INGLLogger;
}
namespace CodeGen
{
class StructBuilder;
class GeneratorGrantleeBase : public QObject
{
Q_OBJECT
public:
virtual ~GeneratorGrantleeBase();
virtual bool init(const XmlReader* reader, StructBuilder* structBuilder) = 0;
bool setTemplate(const QString& templateName, const QString& tmpl);
QString render() const;
QString errorString() const;
bool generateData(const QString& templateName, const QString& outDirectory);
protected:
explicit GeneratorGrantleeBase(const char* loggerName);
bool initInternal(const XmlReader* reader, StructBuilder* structBuilder);
bool readFile(const QString& filename, QString& data) const;
bool getFilenameFromData(const QString& dataToParse, QString& filename) const;
void addCustomFunctions(const QVector<CustomServiceFunctionInformation>& customFunctions);
void exportUsedServices(const XmlReader* reader);
protected:
KTextTemplate::Engine* m_engine;
KTextTemplate::Context* m_ctx;
KTextTemplate::Template m_template;
StructBuilder* m_structBuilder;
NGL::INGLLogger& m_logger;
};
} // namespace CodeGen
#endif // GENERATORGRANTLEEBASE_H
@@ -1,20 +0,0 @@
#ifndef GRANTLEEPARSERSMOKE_H
#define GRANTLEEPARSERSMOKE_H
/**
* @brief GrantleeParserSmoke - Minimal header for test compilation
*
* This header exists solely to allow test_GrantleeParserSmoke.cpp to compile.
* The actual Grantlee parser smoke test is implemented in
* Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp as a standalone executable.
*
* This file was accidentally deleted and needs to be restored for BUILD_TESTING.
*/
namespace GrantleeParserSmoke
{
// Placeholder namespace for test compilation
// Actual implementation is in Lts/Common/CodeGen/tests/GrantleeParserSmoke.cpp
}
#endif // GRANTLEEPARSERSMOKE_H
@@ -1,203 +0,0 @@
#include "GrantleeWrappers.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <utility>
using namespace CodeGen;
#ifdef RTI_HEADER_AVAILABLE
QString IdlStructWrapper::extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity)
{
switch (extensiblity.underlying())
{
case dds::core::xtypes::ExtensibilityKind_def::type::FINAL:
return QStringLiteral("FINAL_EXTENSIBILITY");
case dds::core::xtypes::ExtensibilityKind_def::type::MUTABLE:
return QStringLiteral("MUTABLE_EXTENSIBILITY");
case dds::core::xtypes::ExtensibilityKind_def::type::EXTENSIBLE:
return QStringLiteral("EXTENSIBLE_EXTENSIBILITY");
}
Q_ASSERT_X(false, "IdlStructWrapper::extensibility2String", "Unknown extensiblity kind");
return QString();
}
#else
QString IdlStructWrapper::extensibility2String(int /*extensiblity*/)
{
// RTI not available - return placeholder
return QStringLiteral("EXTENSIBLE_EXTENSIBILITY");
}
#endif
QString DataTopicWrapper::typeFq() const
{
const QString& t = type();
const QString& s = serviceName();
if (s.isEmpty())
return t;
if (t.startsWith(s))
return t;
return s + QStringLiteral("::") + t;
}
QString MemberInformationWrapper::ddsToQVariant() const
{
// rtiddsgen 7.7 generates plain public members - access them directly.
return QStringLiteral("toQVariant(data.") + cppMemberName() + QStringLiteral(")");
}
QString MemberInformationWrapper::qVariantToDds() const
{
const QString ddsType =
isOptional() ? (QStringLiteral("dds::core::optional<") + cppDdsType() + QLatin1Char('>')) : (cppDdsType());
return QStringLiteral("data.") + cppMemberName() + QStringLiteral(" = fromQVariant<") + ddsType +
QStringLiteral(">(q.value(i++))");
}
QString MemberInformationWrapper::ddsToJsonElement() const
{
if (cppMemberName().isEmpty())
return QStringLiteral("obj[QStringLiteral(\"") + cppType() + QStringLiteral("\")] = element;");
// rtiddsgen 7.7 generates plain public members for every kind of
// member (struct or not), so no getter call is emitted.
const QString memberAccess = cppMemberName();
if (!isOptional())
return QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + cppToJsonPre() +
QStringLiteral("element.") + memberAccess + cppToJsonPost();
const QString optionalAccess = cppMemberName() + QStringLiteral(".is_set()");
const QString getValue = cppMemberName();
return QStringLiteral("if (element.") + optionalAccess + QStringLiteral(") ") +
QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + cppToJsonPre() +
QStringLiteral("*element.") + getValue + cppToJsonPost();
}
QString MemberInformationWrapper::jsonElementToDds() const
{
if (cppMemberName().isEmpty())
return QStringLiteral("element = ") + cppFromQvPre2() + QStringLiteral("objElement.value(QStringLiteral(\"") +
cppType() + QStringLiteral("\"))") + cppFromJsonPost();
QString ret(QStringLiteral("const QJsonValue val(objElement.value(QStringLiteral(\"") + cppMemberName() +
QStringLiteral("\"))); "));
if (isOptional())
ret += QStringLiteral("if (!val.isNull()) ");
// rtiddsgen 7.7 generates plain public members - assign instead of
// calling a setter.
ret += QStringLiteral("element.") + cppMemberName() + QStringLiteral(" = ") + cppFromJsonPre2() +
QStringLiteral("val") + cppFromJsonPost();
return ret;
}
QString MemberInformationWrapper::dbToJsonElement() const
{
if (cppMemberName().isEmpty())
return QStringLiteral("obj[QStringLiteral(\"") + cppType() + QStringLiteral("\")] = element;");
if (isOptional() || cppType() == QStringLiteral("QDateTime"))
{
QString ret = QStringLiteral("if (") +
((cppType() == QStringLiteral("QDateTime")) ? QString() : QStringLiteral("!")) +
QStringLiteral("element.") + cppMemberName() +
((cppType() == QStringLiteral("QDateTime")) ? QStringLiteral(".isValid()) ")
: QStringLiteral(".isNull()) ")) +
QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ");
if (isOptional())
return ret += cppFromQvPre2() + QStringLiteral("element.") + cppMemberName() + cppFromQvPost();
}
return QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + cppToJsonPre() +
QStringLiteral("element.") + cppMemberName() + cppToJsonPost();
}
QString MemberInformationWrapper::jsonElementToDb() const
{
if (cppMemberName().isEmpty())
return QStringLiteral("element = ") + cppFromQvPre2() + QStringLiteral("objElement.value(QStringLiteral(\"") +
cppType() + QStringLiteral("\"))") + cppFromJsonPost();
QString ret(QStringLiteral("const QJsonValue val(objElement.value(QStringLiteral(\"") + cppMemberName() +
QStringLiteral("\"))); "));
if (isOptional())
ret += QStringLiteral("if (!val.isUndefined()) ");
ret += QStringLiteral("element.") + cppMemberName() + QStringLiteral(" = ") + cppFromJsonPre2() +
QStringLiteral("val") + cppFromJsonPost();
return ret;
}
QVariantList DbStructWrapper::insertStructures()
{
QStringList tableNames;
QMap<QString, QVector<MemberInformationWrapper*>> structures;
QMap<QString, MemberInformationWrapper*> primaryKeys;
QMap<QString, MemberInformationWrapper*> fkComplexTableKeys;
tableNames.reserve(5);
for (const QVariant& v : std::as_const(m_member))
{
MemberInformationWrapper* mi = v.value<MemberInformationWrapper*>();
const QString& tableName = mi->info().db.table;
if (mi->info().db.usedForInsert)
{
auto& tnStructure = structures[tableName];
tnStructure.append(mi);
if (!tableNames.contains(tableName))
tableNames.push_back(tableName);
}
if (mi->info().fk)
fkComplexTableKeys[mi->info().fk->destination.tableName] = mi;
if (mi->info().db.isPrimaryKey)
{
if (!primaryKeys.contains(tableName))
primaryKeys[tableName] = mi;
}
if (mi->info().db.isAssignmentForPrimaryTablePk)
fkComplexTableKeys[mi->info().db.table] = mi;
}
QVariantList vl;
vl.reserve(tableNames.size());
for (int i = 0; i < tableNames.size(); ++i)
{
const QString& tn = tableNames.at(i);
MemberInformationWrapper* serialMember = fkComplexTableKeys.value(tn);
MemberInformationWrapper* baseId = nullptr;
MemberInformationWrapper* primaryKey = primaryKeys.value(tn);
if (i == 0 && tableNames.size() > 1)
baseId = primaryKeys.value(tn);
if (tableNames.size() == 1)
{
baseId = nullptr;
if (!primaryKey)
{
NGL::INGLLogger& logger = NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("DbStructWrapper");
WARNING_LOG(logger) << "Table " << tn << " has no primary key!";
}
else
{
if (primaryKey->dbHasSequence())
serialMember = primaryKey;
}
}
const QVector<MemberInformationWrapper*> members = structures.value(tn);
TableWrapper* tw = new TableWrapper(tn, members, serialMember, baseId, primaryKey, this);
vl += QVariant::fromValue(tw);
}
return vl;
}
@@ -1,932 +0,0 @@
#pragma once
#include "StructBuilder.h"
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QVariant>
#include <utility>
#ifdef RTI_HEADER_AVAILABLE
#include <dds/core/xtypes/EnumType.hpp>
#endif
namespace CodeGen
{
class NameWrapper : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name)
Q_PROPERTY(QString nameCc READ nameCc)
Q_PROPERTY(QString nameFlc READ nameFlc)
public:
NameWrapper(const QString& name, QObject* parent) : QObject(parent), m_name(name) {}
QString name() const
{
return m_name;
}
QString nameCc() const
{
return StructBuilder::camelCaseString(name(), true);
}
QString nameFlc() const
{
return StructBuilder::firstCharLowercase(nameCc());
}
private:
const QString m_name;
};
class VariantListWrapper : public NameWrapper
{
Q_OBJECT
Q_PROPERTY(QVariantList member READ member)
public:
using NameWrapper::NameWrapper;
~VariantListWrapper()
{
for (const QVariant& v : std::as_const(m_member))
{
auto ptr = v.value<QObject*>();
delete ptr;
}
}
QVariantList member() const
{
return m_member;
}
protected:
QVariantList m_member;
};
class MemberInformationWrapper final : public QObject
{
Q_OBJECT
Q_PROPERTY(QString cppMemberName READ cppMemberName)
Q_PROPERTY(QString cppMemberNameCc READ cppMemberNameCc)
Q_PROPERTY(QString cppStructName READ cppStructName)
Q_PROPERTY(QString cppType READ cppType)
Q_PROPERTY(QString cppToQvPre READ cppToQvPre)
Q_PROPERTY(QString cppToQvPost READ cppToQvPost)
Q_PROPERTY(QString cppFromQvPre1 READ cppFromQvPre1)
Q_PROPERTY(QString cppFromQvPre2 READ cppFromQvPre2)
Q_PROPERTY(QString cppFromQvPost READ cppFromQvPost)
Q_PROPERTY(QString cppDefault READ cppDefault)
Q_PROPERTY(QString dbTableName READ dbTableName)
Q_PROPERTY(QString dbColumnName READ dbColumnName)
Q_PROPERTY(QString dbColumnNameView READ dbColumnNameView)
Q_PROPERTY(QString ddsToQVariant READ ddsToQVariant)
Q_PROPERTY(QString qVariantToDds READ qVariantToDds)
Q_PROPERTY(QString ddsToJsonElement READ ddsToJsonElement)
Q_PROPERTY(QString jsonElementToDds READ jsonElementToDds)
Q_PROPERTY(QString dbToJsonElement READ dbToJsonElement)
Q_PROPERTY(QString jsonElementToDb READ jsonElementToDb)
Q_PROPERTY(bool dbHasSequence READ dbHasSequence)
Q_PROPERTY(bool isOptional READ isOptional)
Q_PROPERTY(bool isCppStruct READ isCppStruct)
public:
MemberInformationWrapper(const MemberInformation& mi, QObject* parent) : QObject(parent), m_mi(mi) {}
QString cppMemberName() const
{
return m_mi.cpp.memberName;
}
QString cppMemberNameCc() const
{
return StructBuilder::camelCaseString(m_mi.cpp.memberName, true);
}
QString cppStructName() const
{
return m_mi.cpp.structName;
}
QString cppType() const
{
return m_mi.cpp.type;
}
QString cppToQvPre() const
{
return m_mi.cpp.toVariantPre;
}
QString cppToQvPost() const
{
return m_mi.cpp.toVariantPost;
}
QString cppFromQvPre1() const
{
return m_mi.cpp.fromVariantPre1;
}
QString cppFromQvPre2() const
{
return m_mi.cpp.fromVariantPre2;
}
QString cppFromQvPost() const
{
return m_mi.cpp.fromVariantPost;
}
QString cppDefault() const
{
return m_mi.cpp.defaultValue;
}
QString dbTableName() const
{
return m_mi.db.table;
}
QString dbColumnName() const
{
return m_mi.db.columnName;
}
QString dbColumnNameView() const
{
return m_mi.db.getColumnNameView();
}
QString cppDdsType() const
{
return m_mi.cpp.ddsType.isEmpty() ? cppType() : m_mi.cpp.ddsType;
}
QString ddsToQVariant() const;
QString qVariantToDds() const;
QString ddsToJsonElement() const;
QString jsonElementToDds() const;
QString dbToJsonElement() const;
QString jsonElementToDb() const;
bool dbHasSequence() const
{
return m_mi.db.hasSequence;
}
bool isOptional() const
{
return m_mi.cpp.isOptional;
}
bool isCppStruct() const
{
return m_mi.cpp.usedForCppStruct;
}
QString cppToJsonPre() const
{
return m_mi.cpp.toJsonPre.isNull() ? m_mi.cpp.toVariantPre : m_mi.cpp.toJsonPre;
}
QString cppToJsonPost() const
{
return m_mi.cpp.toJsonPost.isNull() ? m_mi.cpp.toVariantPost : m_mi.cpp.toJsonPost;
}
QString cppFromJsonPre2() const
{
return m_mi.cpp.fromJsonPre2.isNull() ? m_mi.cpp.fromVariantPre2 : m_mi.cpp.fromJsonPre2;
}
QString cppFromJsonPost() const
{
return m_mi.cpp.fromJsonPost.isNull() ? m_mi.cpp.fromVariantPost : m_mi.cpp.fromJsonPost;
}
MemberInformation info() const
{
return m_mi;
}
private:
const MemberInformation m_mi;
};
class MemberInformationListWrapper : public VariantListWrapper
{
Q_OBJECT
Q_PROPERTY(QVariantList cppMember READ cppMember)
Q_PROPERTY(QVariantList jsonMember READ jsonMember)
Q_PROPERTY(QVariantList fkMember READ fkMember)
Q_PROPERTY(QVariantList selectRemoveMember READ selectRemoveMember)
Q_PROPERTY(QVariantList viewMember READ viewMember)
Q_PROPERTY(MemberInformationWrapper* ddsKey READ ddsKey)
Q_PROPERTY(MemberInformationWrapper* primaryKey READ primaryKey)
public:
explicit MemberInformationListWrapper(const QString& name, const QVector<MemberInformation>& member,
QObject* parent)
: VariantListWrapper(name, parent)
{
for (const MemberInformation& mi : member)
m_member += QVariant::fromValue(new MemberInformationWrapper(mi, this));
}
QVariantList memberByFilter(const std::function<bool(const MemberInformation& mi)>& acceptFilter) const
{
QVariantList vl;
vl.reserve(m_member.size());
for (const QVariant& v : m_member)
{
MemberInformationWrapper* mi = v.value<MemberInformationWrapper*>();
if (mi && acceptFilter(mi->info()))
vl += v;
}
return vl;
}
QVariantList cppMember() const
{
return memberByFilter([](const MemberInformation& mi) { return mi.cpp.usedForCppStruct; });
}
QVariantList jsonMember() const
{
return memberByFilter([](const MemberInformation& mi) { return mi.cpp.usedForJson; });
}
QVariantList fkMember() const
{
return memberByFilter([](const MemberInformation& mi) { return mi.db.isForeignKey; });
}
QVariantList selectRemoveMember() const
{
return memberByFilter([](const MemberInformation& mi) { return mi.db.generateSelectRemove; });
}
QVariantList viewMember() const
{
return memberByFilter([](const MemberInformation& mi) { return mi.db.usedForView; });
}
MemberInformationWrapper* ddsKey() const
{
for (const QVariant& v : m_member)
{
MemberInformationWrapper* mi = v.value<MemberInformationWrapper*>();
if (mi->info().isDdsKey)
return mi;
}
return nullptr;
}
MemberInformationWrapper* primaryKey() const
{
for (const QVariant& v : m_member)
{
MemberInformationWrapper* mi = v.value<MemberInformationWrapper*>();
if (mi->info().db.isPrimaryKey)
return mi;
}
return nullptr;
}
};
class TableWrapper final : public NameWrapper
{
Q_OBJECT
Q_PROPERTY(QVector<MemberInformationWrapper*> member READ member)
Q_PROPERTY(MemberInformationWrapper* serialMember READ serialMember)
Q_PROPERTY(MemberInformationWrapper* baseId READ baseId)
Q_PROPERTY(MemberInformationWrapper* primaryKey READ primaryKey)
public:
explicit TableWrapper(const QString& name, const QVector<MemberInformationWrapper*>& member,
MemberInformationWrapper* serialMember, MemberInformationWrapper* baseId,
MemberInformationWrapper* primaryKey, QObject* parent)
: NameWrapper(name, parent), m_member(member), m_serialMember(serialMember), m_baseId(baseId),
m_primaryKey(primaryKey)
{
}
QVector<MemberInformationWrapper*> member() const
{
return m_member;
}
MemberInformationWrapper* serialMember() const
{
return m_serialMember;
}
MemberInformationWrapper* baseId() const
{
return m_baseId;
}
MemberInformationWrapper* primaryKey() const
{
return m_primaryKey;
}
private:
QVector<MemberInformationWrapper*> m_member;
MemberInformationWrapper* m_serialMember;
MemberInformationWrapper* m_baseId;
MemberInformationWrapper* m_primaryKey;
};
class DbStructWrapper final : public MemberInformationListWrapper
{
Q_OBJECT
Q_PROPERTY(QVariantList insertStructures READ insertStructures)
Q_PROPERTY(QString viewTableName READ viewTableName)
Q_PROPERTY(bool deleteWithoutPkAllowed READ deleteWithoutPkAllowed)
Q_PROPERTY(MemberInformationWrapper* primaryKey READ primaryKey)
Q_PROPERTY(QVariantList dbNotifications READ dbNotifications)
public:
explicit DbStructWrapper(const CompleteStructInformation& csi, QObject* parent)
: MemberInformationListWrapper(csi.structName, csi.member, parent), m_csi(csi)
{
m_dbNotifications.reserve(m_csi.dbNotifications.size());
for (const auto& notification : std::as_const(m_csi.dbNotifications))
m_dbNotifications += QVariant(notification.methodName);
}
MemberInformationWrapper* primaryKey() const
{
for (const QVariant& v : m_member)
{
MemberInformationWrapper* mi = v.value<MemberInformationWrapper*>();
if (mi->info().db.isPrimaryKey && mi->info().db.table == primaryTableName())
return mi;
}
return nullptr;
}
QVariantList insertStructures();
bool deleteWithoutPkAllowed() const
{
return m_csi.tables.size() == 1;
}
QVariantList dbNotifications() const
{
return m_dbNotifications;
}
QString primaryTableName() const
{
return m_csi.primaryTable->name;
}
QString viewTableName() const
{
return (m_csi.tables.size() == 1) ? primaryTableName() : m_csi.viewName;
}
private:
const CompleteStructInformation m_csi;
QVariantList m_dbNotifications;
};
class CustomFunctionWrapper final : public QObject
{
Q_OBJECT
Q_PROPERTY(QString signature READ signature)
public:
explicit CustomFunctionWrapper(const QString& signature, QObject* parent) : QObject(parent), m_signature(signature)
{
}
QString signature() const
{
return m_signature;
}
private:
const QString m_signature;
};
class IdlEnumKeyValueWrapper final : public QObject
{
Q_OBJECT
Q_PROPERTY(QString key READ key)
Q_PROPERTY(int value READ value)
public:
explicit IdlEnumKeyValueWrapper(const EnumKeyValuePair& ekvp, QObject* parent) : QObject(parent), m_ekvp(ekvp) {}
QString key() const
{
return m_ekvp.key;
}
int value() const
{
return m_ekvp.value;
}
private:
const EnumKeyValuePair m_ekvp;
};
class IdlDataWrapper final : public NameWrapper
{
Q_OBJECT
Q_PROPERTY(QString type READ type)
Q_PROPERTY(QString comment READ comment)
Q_PROPERTY(QString isKey READ isKey)
Q_PROPERTY(QString isOptional READ isOptional)
Q_PROPERTY(QString cppType READ cppType)
Q_PROPERTY(QString cppTypeCr READ cppTypeCr)
Q_PROPERTY(QString nameGetter READ nameGetter)
public:
IdlDataWrapper(const IdlData& data, QObject* parent) : NameWrapper(data.name, parent), m_data(data) {}
QString type() const
{
return m_data.dataTypeInfo->idlType();
}
QString comment() const
{
return m_data.comment;
}
QString isKey() const
{
return m_data.isKey ? QStringLiteral(" \t //@key") : QString();
}
QString isOptional() const
{
return m_data.isOptional ? QStringLiteral(" \t //@Optional") : QString();
}
QString cppType() const
{
return m_data.dataTypeInfo->cppType();
}
QString cppTypeCr() const
{
return m_data.dataTypeInfo->cppTypeConstRef();
}
QString nameGetter() const
{
// rtiddsgen 7.7 generates public data members (no getters); optional
// members are omg::types::optional<T> and are unwrapped via .get().
return m_data.name + (m_data.isOptional ? QStringLiteral(".get()") : QString());
}
private:
const IdlData m_data;
};
class IdlEnumWrapper final : public VariantListWrapper
{
Q_OBJECT
public:
explicit IdlEnumWrapper(const EnumInformation& ei, QObject* parent) : VariantListWrapper(ei.enumName, parent)
{
m_member.reserve(ei.values.size());
for (const auto& val : ei.values)
m_member += QVariant::fromValue(new IdlEnumKeyValueWrapper(val, this));
}
};
class IdlStructWrapper final : public VariantListWrapper
{
Q_OBJECT
Q_PROPERTY(QString comment READ comment)
Q_PROPERTY(QString baseValueType READ baseValueType)
Q_PROPERTY(QString extensibility READ extensibility)
public:
explicit IdlStructWrapper(const CompleteTopicTypeInformation& ctti, QObject* parent)
: VariantListWrapper(ctti.name, parent), m_ctti(ctti)
{
m_member.reserve(m_ctti.members.size());
for (const auto& val : std::as_const(m_ctti.members))
m_member += QVariant::fromValue(new IdlDataWrapper(val, this));
}
QString comment() const
{
return m_ctti.comment;
}
QString baseValueType() const
{
return m_ctti.baseValueType;
}
QString extensibility() const
{
return extensibility2String(m_ctti.extensibility);
}
private:
const CompleteTopicTypeInformation m_ctti;
#ifdef RTI_HEADER_AVAILABLE
static QString extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity);
#else
static QString extensibility2String(int extensiblity);
#endif
};
class IdlServiceWrapper final : public QObject
{
Q_OBJECT
Q_PROPERTY(QString svcName READ svcName)
Q_PROPERTY(QString baseFilePath READ baseFilePath)
public:
IdlServiceWrapper(const QString& svcName, const QString& baseFilePath)
: m_svcName(svcName), m_baseFilePath(baseFilePath)
{
}
QString svcName() const
{
return m_svcName;
}
QString baseFilePath() const
{
return m_baseFilePath;
}
private:
const QString m_svcName;
const QString m_baseFilePath;
};
class FunctionTopicWrapper final : public NameWrapper
{
Q_OBJECT
Q_PROPERTY(QString onReply READ onReply)
Q_PROPERTY(QString onRequest READ onRequest)
Q_PROPERTY(QString onError READ onError)
Q_PROPERTY(bool isCoreFt READ isCoreFt)
Q_PROPERTY(bool routeInReplay READ routeInReplay)
Q_PROPERTY(QString reply READ reply)
Q_PROPERTY(QString replyFlc READ replyFlc)
Q_PROPERTY(QString replyHandler READ replyHandler)
Q_PROPERTY(QString replyHandlerFlc READ replyHandlerFlc)
Q_PROPERTY(QString replyType READ replyType)
Q_PROPERTY(QString replyTypeFro READ replyTypeFro)
Q_PROPERTY(QVariantList replyParams READ replyParams)
Q_PROPERTY(QString replyHandlerHash READ replyHandlerHash WRITE setReplyHandlerHash)
Q_PROPERTY(QString request READ request)
Q_PROPERTY(QString requestFlc READ requestFlc)
Q_PROPERTY(QString requestHandler READ requestHandler)
Q_PROPERTY(QString requestHandlerFlc READ requestHandlerFlc)
Q_PROPERTY(QString requestType READ requestType)
Q_PROPERTY(QVariantList requestParams READ requestParams)
public:
FunctionTopicWrapper(const FunctionTopic& ft, const CompleteTopicTypeInformation& siRequest,
const CompleteTopicTypeInformation& siReply, QObject* parent)
: NameWrapper(ft.name, parent), m_ft(ft), m_request(siRequest), m_reply(siReply)
{
}
QString onReply() const
{
return QLatin1String("on") + m_ft.replyTopicName;
}
QString onRequest() const
{
return QLatin1String("on") + m_ft.requestTopicName;
}
QString onError() const
{
return QLatin1String("on") + m_ft.name + QLatin1String("Error");
}
bool isCoreFt() const
{
return (m_ft.replyTopicType == QStringLiteral("Core::FunctionReply") ||
m_ft.replyTopicType == QStringLiteral("FunctionReply"));
}
bool routeInReplay() const
{
return m_ft.routeInReplay;
}
QString reply() const
{
return m_ft.replyTopicName;
}
QString replyFlc() const
{
return StructBuilder::firstCharLowercase(reply());
}
QString replyHandler() const
{
return m_ft.replyTopicName + QLatin1String("Handler");
}
QString replyHandlerFlc() const
{
return StructBuilder::firstCharLowercase(replyHandler());
}
QString replyType() const
{
return m_reply.name;
}
QString replyTypeFro() const
{
return isCoreFt() ? QStringLiteral("Core::FunctionReplyObject") : m_reply.name;
}
QString request() const
{
return m_ft.requestTopicName;
}
QString requestFlc() const
{
return StructBuilder::firstCharLowercase(request());
}
QString requestHandler() const
{
return m_ft.requestTopicName + QLatin1String("Handler");
}
QString requestHandlerFlc() const
{
return StructBuilder::firstCharLowercase(requestHandler());
}
QString requestType() const
{
return m_request.name;
}
void addRequestParams(const QVector<IdlData>& params)
{
for (const auto& param : params)
m_requestParams.push_back(QVariant::fromValue(new IdlDataWrapper(param, this)));
}
QVariantList requestParams() const
{
return m_requestParams;
}
void addReplyParams(const QVector<IdlData>& params)
{
for (const auto& param : params)
m_replyParams.push_back(QVariant::fromValue(new IdlDataWrapper(param, this)));
}
QVariantList replyParams() const
{
return m_replyParams;
}
QString replyHandlerHash() const
{
return m_replyHandlerHash;
}
void setReplyHandlerHash(const QString& hash)
{
m_replyHandlerHash = hash;
}
private:
const FunctionTopic m_ft;
const CompleteTopicTypeInformation m_request;
const CompleteTopicTypeInformation m_reply;
QVariantList m_requestParams;
QVariantList m_replyParams;
QString m_replyHandlerHash;
};
class FunctionTopicsWrapper final : public VariantListWrapper
{
Q_OBJECT
public:
Q_PROPERTY(QVariantList topicsToRouteInReplay READ topicsToRouteInReplay)
using VariantListWrapper::VariantListWrapper;
FunctionTopicWrapper* addFunctionTopic(const FunctionTopic& ft, const CompleteTopicTypeInformation& siRequest,
const CompleteTopicTypeInformation& siReply)
{
FunctionTopicWrapper* ret = new FunctionTopicWrapper(ft, siRequest, siReply, this);
m_member += QVariant::fromValue(ret);
return ret;
}
QVariantList topicsToRouteInReplay() const
{
QVariantList vl;
vl.reserve(m_member.size());
for (const QVariant& v : m_member)
{
FunctionTopicWrapper* ft = v.value<FunctionTopicWrapper*>();
if (ft->routeInReplay())
vl += v;
}
return vl;
}
};
class DataTopicWrapper final : public NameWrapper
{
Q_OBJECT
Q_PROPERTY(QString serviceName READ serviceName)
Q_PROPERTY(QString type READ type)
Q_PROPERTY(QString typeFq READ typeFq)
Q_PROPERTY(QVariant key READ key)
Q_PROPERTY(bool dtcImpl READ dtcImpl)
Q_PROPERTY(bool dtsImpl READ dtsImpl)
Q_PROPERTY(bool recordTopic READ recordTopic)
Q_PROPERTY(QVariant recTable READ recTable)
Q_PROPERTY(QVariantList trColumns READ trColumns)
Q_PROPERTY(bool hasTrColumns READ hasTrColumns)
Q_PROPERTY(bool routeToClient READ routeToClient)
Q_PROPERTY(bool routeToServer READ routeToServer)
Q_PROPERTY(bool routeInReplay READ routeInReplay)
Q_PROPERTY(bool autosubscribe READ isAutoSubscribe)
public:
DataTopicWrapper(const CompleteTopicTypeInformation& ctti, QObject* parent)
: NameWrapper(ctti.name, parent), m_ctti(ctti), m_keyData(nullptr), m_recordData(nullptr)
{
}
void setKeyColumnData(const IdlData& data)
{
delete m_keyData;
m_keyData = new IdlDataWrapper(data, this);
}
void setTrColumns(const QVector<IdlData>& params)
{
for (const auto& param : params)
m_trColumns.push_back(QVariant::fromValue(new IdlDataWrapper(param, this)));
}
QVariantList trColumns() const
{
return m_trColumns;
}
bool hasTrColumns() const
{
return !m_trColumns.empty();
}
void setRecordTableInformation(const CompleteTableInformation& cti)
{
delete m_recordData;
m_recordData = new MemberInformationListWrapper(cti.name, cti.columns, this);
m_ctti.recordTopic = true;
}
QString serviceName() const
{
return m_ctti.serviceName;
}
QString type() const
{
return m_ctti.name;
}
QString typeFq() const;
QVariant key() const
{
return m_keyData ? QVariant::fromValue(m_keyData) : QVariant();
}
bool dtsImpl() const
{
return m_ctti.dtsAutogen;
}
bool dtcImpl() const
{
return m_ctti.dtcAutogen;
}
bool recordTopic() const
{
return m_ctti.recordTopic && m_recordData;
}
bool routeToClient() const
{
return m_ctti.routeToClient;
}
bool routeToServer() const
{
return m_ctti.routeToServer;
}
bool routeInReplay() const
{
return m_ctti.routeInReplay;
}
bool isAutoSubscribe() const
{
return m_ctti.autosubscribe;
}
QVariant recTable() const
{
return recordTopic() ? QVariant::fromValue(m_recordData) : QVariant();
}
private:
CompleteTopicTypeInformation m_ctti;
IdlDataWrapper* m_keyData;
MemberInformationListWrapper* m_recordData;
QVariantList m_trColumns;
};
class DataTopicsWrapper final : public VariantListWrapper
{
Q_OBJECT
public:
Q_PROPERTY(QVariantList topicsToRouteInReplay READ topicsToRouteInReplay)
Q_PROPERTY(QVariantList topicsToRouteToClient READ topicsToRouteToClient)
Q_PROPERTY(QVariantList topicsToRouteToServer READ topicsToRouteToServer)
using VariantListWrapper::VariantListWrapper;
DataTopicWrapper* addDataTopic(const CompleteTopicTypeInformation& ctti)
{
DataTopicWrapper* ret = new DataTopicWrapper(ctti, this);
m_member += QVariant::fromValue(ret);
return ret;
}
QVariantList memberByFilter(const std::function<bool(const DataTopicWrapper* dtw)>& acceptFilter) const
{
QVariantList vl;
vl.reserve(m_member.size());
for (const QVariant& v : m_member)
{
DataTopicWrapper* dtw = v.value<DataTopicWrapper*>();
if (dtw && acceptFilter(dtw))
vl += v;
}
return vl;
}
QVariantList topicsToRouteInReplay() const
{
return memberByFilter([](const DataTopicWrapper* dtw) { return dtw->routeInReplay(); });
}
QVariantList topicsToRouteToClient() const
{
return memberByFilter([](const DataTopicWrapper* dtw) { return dtw->routeToClient(); });
}
QVariantList topicsToRouteToServer() const
{
return memberByFilter([](const DataTopicWrapper* dtw) { return dtw->routeToServer(); });
}
};
class IdlServicesWrapper final : public VariantListWrapper
{
Q_OBJECT
public:
using VariantListWrapper::VariantListWrapper;
void addService(const QString& svcName, const QString& baseFilePath)
{
m_member += QVariant::fromValue(new IdlServiceWrapper(svcName, baseFilePath));
}
};
} // namespace CodeGen
@@ -1,37 +0,0 @@
#
# Hinweise für den CodeGen
#
# $Id$
# $HeadURL$
# $Author$
#
Erstellt: Mai 2022, Norbert Thoden
Der Aufruf des CodeGen kann z.B. so aussehen:
codegen -s /tmp/ppm_serviceORG.xml -i /shd/CTC/TOOLS/TDS/TDS-build/service_idl_files.txt -a dds_topics -o /tmp /shd/CTC/TOOLS/TDS/TDS-source/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee
Edit Juni 2023, Niklas Schmidt
Ebenso ist es nicht möglich einen Struct aus einem anderen Service(Service B) zu verwenden,
der in sich widerum einen anderen Struct aus dem anderen Service (Service B) beinhaltet.
Bsp:
Service A = Struct1{ int id,
Struct2 test
}
Service B = Struct2{
int id2,
Struct3 test
}
Service B = Struct3{
std::string test
}
Mögliche Diagnose:
Wenn Struct3 aus Service A aufgerufen wird, ist wahrscheinlich nicht mehr gekennzeichnet dass der Struct3 aus Service B kommt. Dieser kann somit nicht
aufgelöst werden
@@ -1,892 +0,0 @@
#include "SqlGenerator.h"
#include "StructBuilder.h"
#include "XmlReader.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtCore/QStringList>
#include <QtCore/QFile>
#include <QtCore/QTextStream>
using namespace CodeGen;
static QString getExerciseSchemaName(int exerciseId)
{
return QStringLiteral("ex_") + QString::number(exerciseId);
}
SqlGenerator::SqlGenerator(XmlReader* xmlReader, StructBuilder* structBuilder)
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")),
m_xmlReader(xmlReader), m_structBuilder(structBuilder), m_createdByXmlFile(false), m_isValid(true),
m_exerciseId(0)
{
}
SqlGenerator::SqlGenerator(const QString& xmlFile)
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator")),
m_xmlReader(new XmlReader), m_structBuilder(new StructBuilder(m_xmlReader)), m_createdByXmlFile(true),
m_isValid(false), m_exerciseId(0)
{
m_isValid = m_xmlReader->readServiceFile(xmlFile, QString()) && m_structBuilder->buildStructures();
}
SqlGenerator::~SqlGenerator()
{
if (m_createdByXmlFile)
{
delete m_xmlReader;
delete m_structBuilder;
}
}
bool SqlGenerator::generateData(int exerciseId)
{
DEBUG_LOG(m_logger) << "Generating SQL data for service " << getServiceAbbr() << " in exercise " << exerciseId
<< ", this: " << this;
m_exerciseId = exerciseId;
m_sqlGlobal = SqlStatements();
m_sqlExercise = SqlStatements();
m_sqlItemsGlobal = SqlItems();
m_sqlItemsExercise = SqlItems();
m_referencedSevices.clear();
if (!handleTables())
return false;
if (!handleViews())
return false;
if (!handleEnumerations())
return false;
return true;
}
bool SqlGenerator::createStatementsForTable(const QString& tableName, const QString& tableNameOut)
{
const QMap<QString, CompleteTableInformation> tables = m_structBuilder->getDbTables();
for (const CompleteTableInformation& cti : tables)
{
if (cti.name != tableName)
continue;
CompleteTableInformation ctiOut = cti;
ctiOut.name = tableNameOut;
return handleTable(ctiOut);
}
return false;
}
bool SqlGenerator::handleTables()
{
const QMap<QString, CompleteTableInformation> tables = m_structBuilder->getDbTables();
for (const CompleteTableInformation& cti : tables)
{
if (!handleTable(cti))
return false;
}
const QMap<QString, CompleteTableInformation> recTables = m_structBuilder->getRecordingTopicTables();
MemberInformation miColumnIsValid;
miColumnIsValid.db.columnName = QStringLiteral("dds_is_alive");
miColumnIsValid.db.datatype = QStringLiteral("boolean");
miColumnIsValid.db.defaultValue = QStringLiteral("true");
MemberInformation miInsertionTime;
miInsertionTime.cpp.type = QStringLiteral("timestamp");
miInsertionTime.db.columnName = QStringLiteral("insert_time");
miInsertionTime.db.defaultValue = QStringLiteral("now()");
miInsertionTime.db.usedForInsert = false;
m_structBuilder->fixupQVariantConversions(miInsertionTime);
ConstraintInformation ci;
ci.name = QStringLiteral("IDX_sample_time");
ci.affectedColumns = QStringList({QStringLiteral("sample_time")});
ci.type = ConstraintInformation::Type::index;
for (const CompleteTableInformation& cti : recTables)
{
CompleteTableInformation ctiTmp(cti);
ctiTmp.columns.push_back(miColumnIsValid);
ctiTmp.columns.push_back(miInsertionTime);
ctiTmp.constraints.push_back(ci);
if (!handleTable(ctiTmp))
return false;
}
return true;
}
bool SqlGenerator::handleViews()
{
const QMap<QString, ViewInformation> views = m_structBuilder->getDbViews();
for (const ViewInformation& vi : views)
{
if (!handleView(vi))
return false;
}
return true;
}
bool SqlGenerator::handleView(const ViewInformation& vi)
{
DEBUG_LOG(m_logger) << "Handle view " << vi.name;
const QString parent = vi.baseTable;
DEBUG_LOG(m_logger) << "Parent = " << parent;
if (!parent.isEmpty())
{
const CompleteStructInformation csi = m_structBuilder->structInformationByTableName(parent);
const QString createViewStmt(QStringLiteral("CREATE OR REPLACE VIEW %1 AS SELECT %2\nFROM %3\nWHERE %4;\n"));
const QString dropViewStmt(QStringLiteral("DROP VIEW IF EXISTS %1;"));
DEBUG_LOG(m_logger) << "csi.primaryTable = " << csi.primaryTable.data()->name;
if (!csi.primaryTable.isNull())
{
QStringList fromTables;
fromTables += vi.baseSchema + QLatin1Char('.') + vi.baseTable;
QStringList whereConditions;
if (vi.filter.contains(QLatin1String("exercise_id")))
whereConditions += QLatin1String("exercise_id=") + QString::number(m_exerciseId);
QStringList fqColumnNames;
for (const MemberInformation& mi : csi.member)
{
if (mi.db.usedForView)
fqColumnNames += mi.db.table + QLatin1Char('.') + mi.db.columnName + QLatin1String(" AS ") +
mi.db.getColumnNameView();
}
DEBUG_LOG(m_logger) << "fqColumnNames = " << fqColumnNames.join(QStringLiteral(", "));
if (!whereConditions.isEmpty())
addToCreateViews(createViewStmt.arg(csi.viewName, fqColumnNames.join(QStringLiteral(", ")),
fromTables.join(QStringLiteral(", ")),
whereConditions.join(QStringLiteral(" AND\n"))),
vi.exerciseDependency);
addToDropViews(dropViewStmt.arg(csi.viewName), vi.exerciseDependency);
}
}
return true;
}
bool SqlGenerator::handleTable(const CompleteTableInformation& cti)
{
DEBUG_LOG(m_logger) << "Handle table " << cti.name;
if (cti.columns.isEmpty())
{
WARNING_LOG(m_logger) << "Table " << cti.name
<< " has no DatabaseServiceStruct - not generating anything for this!";
return true;
}
const QString dropTableStmt(QStringLiteral("DROP TABLE IF EXISTS %1 CASCADE;"));
const QString createTableStmt(QStringLiteral("CREATE TABLE IF NOT EXISTS %1 (%2\n);"));
const QString commentTableStmt(QStringLiteral("COMMENT ON TABLE %1 IS '%2';"));
QStringList tableComments;
addToDropTables(dropTableStmt.arg(cti.name), cti.exerciseDependency);
if ((cti.exerciseDependency & Global) == Global)
{
addToCreateTables(createTableStmt.arg(
cti.name, getTableColumnCreateStatements(cti.name, cti.columns, &tableComments, Global)),
Global);
}
if ((cti.exerciseDependency & Exercise) == Exercise)
{
addToCreateTables(createTableStmt.arg(cti.name, getTableColumnCreateStatements(cti.name, cti.columns,
&tableComments, Exercise)),
Exercise);
}
addToTables(cti.name, cti.exerciseDependency);
if (!cti.comment.isEmpty())
addToCreateTables(commentTableStmt.arg(cti.name, fixupComment(cti.comment)), cti.exerciseDependency);
addToCreateTables(tableComments, cti.exerciseDependency);
const QString parent = m_xmlReader->getStruct2BaseClassRelations().value(cti.name);
const CompleteStructInformation csi = m_structBuilder->structInformationByTableName(cti.name);
if (!parent.isEmpty())
{
const QString createViewStmt(QStringLiteral("CREATE OR REPLACE VIEW %1 AS SELECT %2\nFROM %3\nWHERE %4;\n"));
const QString dropViewStmt(QStringLiteral("DROP VIEW IF EXISTS %1;"));
if (!csi.primaryTable.isNull())
{
QStringList fqColumnNames;
QStringList fromTables;
QStringList whereConditions;
fromTables += cti.name;
for (const MemberInformation& mi : csi.member)
{
if (mi.db.usedForView)
fqColumnNames += mi.db.table + QLatin1Char('.') + mi.db.columnName + QLatin1String(" AS ") +
mi.db.getColumnNameView();
if (mi.cpp.isReadOnly && mi.db.isForeignKey)
{
for (const QSharedPointer<TableInformation>& tiPtr : csi.tables)
{
if (tiPtr->name == mi.db.table)
{
const QSharedPointer<ForeignKeyInformation> fki =
tiPtr->fkInformationForColumn(mi.db.table, mi.db.columnName);
if (!fki.isNull())
{
if (!fromTables.contains(fki->destination.tableName))
{
whereConditions += fki->destination.tableName + QLatin1Char('.') +
fki->destination.columnName + QLatin1String(" = ") +
fki->source.tableName + QLatin1Char('.') +
fki->source.columnName;
fromTables += fki->destination.tableName;
}
}
break;
}
}
}
}
if (!whereConditions.isEmpty())
{
addToCreateViews(createViewStmt.arg(csi.viewName, fqColumnNames.join(QStringLiteral(", ")),
fromTables.join(QStringLiteral(", ")),
whereConditions.join(QStringLiteral(" AND\n"))),
cti.exerciseDependency);
}
addToDropViews(dropViewStmt.arg(csi.viewName), cti.exerciseDependency);
}
}
if (!generateDbNotifications(cti.name, csi.dbNotifications, cti.exerciseDependency))
return false;
generateForeignKeys(cti.foreignKeys, cti.exerciseDependency);
generateConstraints(cti.name, cti.constraints, cti.exerciseDependency);
return true;
}
QString SqlGenerator::getTableColumnCreateStatements(const QString& tableName,
const QVector<MemberInformation>& columns,
QStringList* tableComments, ExerciseDependency ed)
{
Q_ASSERT(ed == Global || ed == Exercise);
const QString commentColumnStmt(QStringLiteral("COMMENT ON COLUMN %1 IS '%2';"));
int maxLength = 1;
for (const MemberInformation& mi : columns)
maxLength = std::max(maxLength, static_cast<int>(mi.db.columnName.length()));
maxLength += 1;
QStringList retCols;
retCols.reserve(columns.size() + 1);
retCols += QString();
for (int i = 0; i < columns.size(); ++i)
{
const MemberInformation& mi = columns.at(i);
const FullQualifiedColumnInformation& ci = mi.db;
const QString spaces(maxLength - ci.columnName.length(), QLatin1Char(' '));
QString column = ci.columnName + spaces + ci.datatype;
if (ci.hasSequence)
{
const QString seqDescr = generateSequence(tableName, ci, ed);
QString schema;
if (ed == Global)
schema = m_xmlReader->getServiceAbbr();
else
schema = getExerciseSchemaName(m_exerciseId);
column += QLatin1String(" DEFAULT nextval(( '") + schema + QLatin1Char('.') + seqDescr +
QLatin1String("'::text)::regclass)");
}
if (!ci.canBeNull)
column += QLatin1String(" NOT NULL");
if (!ci.hasSequence && !ci.defaultValue.isEmpty())
column += QLatin1String(" DEFAULT ") + ci.defaultValue;
if (i < columns.size() - 1)
column += QLatin1Char(',');
if (!ci.comment.isEmpty())
{
const QString comment(fixupComment(ci.comment));
const QString fqn = ci.table + QLatin1Char('.') + ci.columnName;
*tableComments += commentColumnStmt.arg(fqn, comment);
column += QLatin1String(" -- ") + comment;
}
retCols += column;
if (ci.isPrimaryKey)
generatePrimaryKey(tableName, ci, ed);
}
return retCols.join(QStringLiteral("\n "));
}
QString SqlGenerator::generateSequence(const QString& tableName, const FullQualifiedColumnInformation& ci,
ExerciseDependency ed)
{
const QString sequenceName = tableName + QLatin1Char('_') + ci.columnName + QLatin1String("_seq");
const QString createSequenceStmt(
QLatin1String("CREATE SEQUENCE IF NOT EXISTS %1 INCREMENT %2 START %3 MINVALUE %4;"));
const int minValue = ci.sequenceStart < 1 ? ci.sequenceStart : 1;
addToCreateSequences(
createSequenceStmt.arg(sequenceName).arg(ci.sequenceIncrement).arg(ci.sequenceStart).arg(minValue), ed);
addToSequences(sequenceName, ed);
return sequenceName;
}
void SqlGenerator::generatePrimaryKey(const QString& tableName, const FullQualifiedColumnInformation& ci,
ExerciseDependency ed)
{
QString pkName = QLatin1String("PK_") + tableName + QLatin1Char('_') + ci.columnName;
if (pkName.size() > 63)
pkName.resize(63);
addToPks(pkName, ed);
const QString pkConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 ADD CONSTRAINT %2 PRIMARY KEY (%3);"));
addToCreatePks(pkConstraint.arg(tableName, pkName, ci.columnName), ed);
const QString pkDropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;"));
addToDropConstraints(pkDropConstraint.arg(tableName, pkName), ed);
}
void SqlGenerator::generateForeignKeys(const QVector<QSharedPointer<ForeignKeyInformation>>& foreignKeys,
ExerciseDependency ed)
{
for (const QSharedPointer<ForeignKeyInformation>& fk : foreignKeys)
generateForeignKey(fk, ed);
}
void SqlGenerator::generateForeignKey(const QSharedPointer<ForeignKeyInformation>& foreignKey, ExerciseDependency ed)
{
QString fkName = QLatin1String("FK_") + foreignKey->source.tableName + QLatin1Char('_') +
foreignKey->source.columnName + QLatin1Char('_') + foreignKey->destination.tableName +
QLatin1Char('_') + foreignKey->destination.columnName;
if (fkName.size() > 63)
fkName = foreignKey->name;
addToFks(fkName, ed);
QString fkConstraint(QStringLiteral("ALTER TABLE %1 ADD CONSTRAINT %2 FOREIGN KEY (%3) REFERENCES %4 (%5)"));
switch (foreignKey->deleteAction)
{
case ForeignKeyInformation::OnDeleteAction::NoAction:
fkConstraint += QStringLiteral(" ON DELETE NO ACTION");
break;
case ForeignKeyInformation::OnDeleteAction::Cascade:
fkConstraint += QStringLiteral(" ON DELETE CASCADE ON UPDATE CASCADE");
break;
case ForeignKeyInformation::OnDeleteAction::Restrict:
fkConstraint += QStringLiteral(" ON DELETE RESTRICT");
break;
case ForeignKeyInformation::OnDeleteAction::SetNull:
fkConstraint += QStringLiteral(" ON DELETE SET NULL");
break;
case ForeignKeyInformation::OnDeleteAction::SetDefault:
fkConstraint += QStringLiteral(" ON DELETE SET DEFAULT");
break;
}
fkConstraint += QLatin1Char(';');
QString destination;
if (!foreignKey->destination.schema.isEmpty())
{
const QString& extSchema = foreignKey->destination.schema;
destination = extSchema + QLatin1Char('.') + foreignKey->destination.tableName;
}
else
destination = foreignKey->destination.tableName;
const QString& extSvcAbbr = foreignKey->destination.serviceAbbr;
if (!extSvcAbbr.isEmpty() && !m_referencedSevices.contains(extSvcAbbr))
m_referencedSevices += extSvcAbbr;
addToCreateFks(fkConstraint.arg(foreignKey->source.tableName, fkName, foreignKey->source.columnName, destination,
foreignKey->destination.columnName),
ed);
const QString fkDropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;"));
addToDropConstraints(fkDropConstraint.arg(foreignKey->source.tableName, fkName), ed);
}
void SqlGenerator::generateConstraints(const QString& tableName, const QVector<ConstraintInformation>& constraints,
ExerciseDependency ed)
{
for (const ConstraintInformation& constraint : constraints)
generateConstraint(tableName, constraint, ed);
}
void SqlGenerator::generateConstraint(const QString& tableName, const ConstraintInformation& constraint,
ExerciseDependency ed)
{
const QString dropConstraint(QStringLiteral("ALTER TABLE IF EXISTS %1 DROP CONSTRAINT IF EXISTS %2 CASCADE;"));
switch (constraint.type)
{
case ConstraintInformation::Type::index: {
QString constraintName = QLatin1String("IDX_") + tableName + QLatin1Char('_') +
constraint.affectedColumns.join(QStringLiteral("_"));
if (constraintName.size() > 63)
constraintName.resize(63);
const QString index(QStringLiteral("CREATE INDEX %1 ON %2 (%3);"));
addToCreateConstraints(
index.arg(constraintName, tableName, constraint.affectedColumns.join(QStringLiteral(", "))), ed);
addToConstraints(constraintName, ed);
addToDropConstraints(dropConstraint.arg(tableName, constraintName), ed);
break;
}
case ConstraintInformation::Type::unique: {
QString constraintName = QLatin1String("UQ_") + tableName + QLatin1Char('_') +
constraint.affectedColumns.join(QStringLiteral("_"));
if (constraintName.size() > 63)
constraintName.resize(63);
const QString unique(QStringLiteral("ALTER TABLE %1 ADD CONSTRAINT %2 UNIQUE (%3);"));
addToCreateConstraints(
unique.arg(tableName, constraintName, constraint.affectedColumns.join(QStringLiteral(", "))), ed);
addToConstraints(constraintName, ed);
addToDropConstraints(dropConstraint.arg(tableName, constraintName), ed);
break;
}
case ConstraintInformation::Type::unknown:
break;
}
}
bool SqlGenerator::handleEnumerations()
{
const QVector<EnumInformation> enumerations = m_xmlReader->getEnumerations();
for (const EnumInformation& ei : enumerations)
{
if (!handleEnumeration(ei))
return false;
}
return true;
}
bool SqlGenerator::handleEnumeration(const EnumInformation& ei)
{
const QStringList sql = handleEnumeration(ei, QString());
if (m_sqlItemsGlobal.tables.contains(ei.tableName))
m_sqlGlobal.enumerationValues += sql;
if (m_sqlItemsExercise.tables.contains(ei.tableName))
m_sqlExercise.enumerationValues += sql;
return true;
}
QStringList SqlGenerator::handleEnumeration(const EnumInformation& ei, const QString& dbSchema) const
{
DEBUG_LOG(m_logger) << "Handle enumeration " << ei.enumName;
if (ei.tableName.isEmpty())
{
INFO_LOG(m_logger) << "No table for enumeration " << ei.enumName << " - not creating inserts for this enum";
return QStringList();
}
const QString insertEnum(QStringLiteral("INSERT INTO %1 (%2) VALUES (%3);"));
const QString columnNames(QStringLiteral("id, name"));
const QString tablename = dbSchema.isEmpty() ? ei.tableName : dbSchema + QLatin1Char('.') + ei.tableName;
QStringList enumerationValues;
for (const EnumKeyValuePair& evp : ei.values)
{
const QString values(QString::number(evp.value) + QLatin1String(", '") + fixupComment(evp.key) +
QLatin1Char('\''));
enumerationValues += insertEnum.arg(tablename, columnNames, values);
}
return enumerationValues;
}
bool SqlGenerator::generateDbNotifications(const QString& tableName,
const QVector<DbNotifcationInformation>& notifications,
ExerciseDependency ed)
{
for (const DbNotifcationInformation& notification : notifications)
{
if (notification.paramNames.isEmpty() || notification.paramNames.size() > 1)
{
ERROR_LOG(m_logger) << "Can not handle more than one parameter";
continue;
}
generateDbNotification(notification, ed);
if (!generateDbNotificationTrigger(tableName, notification, ed))
return false;
}
return true;
}
void SqlGenerator::generateDbNotification(const DbNotifcationInformation& notification, ExerciseDependency ed)
{
const QString func(QStringLiteral("CREATE OR REPLACE FUNCTION public.%1()\n"
"RETURNS TRIGGER AS $$\n"
"DECLARE\n"
" toNotify RECORD;\n"
" toReturn RECORD;\n"
"BEGIN\n"
" IF (TG_OP = 'INSERT') THEN\n"
" toNotify = NEW;\n"
" toReturn = NEW;\n"
" ELSIF (TG_OP = 'UPDATE') THEN\n"
" toNotify = OLD;\n"
" toReturn = NEW;\n"
" ELSIF (TG_OP = 'DELETE') THEN\n"
" toNotify = OLD;\n"
" toReturn = OLD;\n"
" END IF;\n"
" \n"
" PERFORM pg_notify('%2', CAST(toNotify.%3 AS text));\n"
" RETURN toReturn;\n"
"END;\n"
"$$ LANGUAGE plpgsql;"));
const QString triggerFuncName(getNotificationFunctionName(notification));
addToDbFunctions(func.arg(triggerFuncName, notification.methodName, notification.paramNames.first()), ed);
}
bool SqlGenerator::generateDbNotificationTrigger(const QString& tableName, const DbNotifcationInformation& notification,
ExerciseDependency ed)
{
const QString drop(QStringLiteral("DROP TRIGGER IF EXISTS %1 ON %2;"));
const QString func(QStringLiteral("CREATE TRIGGER %1\n") +
QStringLiteral("%2 ON %3 FOR EACH ROW EXECUTE PROCEDURE public.%4();"));
for (DbNotifcationInformation::NotificationType notifyType : notification.notificationType)
{
QString when;
QString triggerNameShortcut;
switch (notifyType)
{
case DbNotifcationInformation::NotificationType::BeforeInsert:
when = QStringLiteral("BEFORE INSERT");
triggerNameShortcut = QStringLiteral("bi");
break;
case DbNotifcationInformation::NotificationType::AfterInsert:
when = QStringLiteral("AFTER INSERT");
triggerNameShortcut = QStringLiteral("ai");
break;
case DbNotifcationInformation::NotificationType::BeforeUpdate:
when = QStringLiteral("BEFORE UPDATE");
triggerNameShortcut = QStringLiteral("bu");
break;
case DbNotifcationInformation::NotificationType::AfterUpdate:
when = QStringLiteral("AFTER UPDATE");
triggerNameShortcut = QStringLiteral("au");
break;
case DbNotifcationInformation::NotificationType::BeforeDelete:
when = QStringLiteral("BEFORE DELETE");
triggerNameShortcut = QStringLiteral("bd");
break;
case DbNotifcationInformation::NotificationType::AfterDelete:
when = QStringLiteral("AFTER DELETE");
triggerNameShortcut = QStringLiteral("ad");
break;
}
const QString triggerName(QLatin1String("trg_") + triggerNameShortcut + QLatin1Char('_') +
notification.methodName);
const QString triggerFuncName(getNotificationFunctionName(notification));
addToDbFunctionTrigger(drop.arg(triggerName, tableName), ed);
addToDbFunctionTrigger(func.arg(triggerName, when, tableName, triggerFuncName), ed);
}
return true;
}
QString SqlGenerator::getNotificationFunctionName(const DbNotifcationInformation& notification) const
{
return getServiceAbbr() + QLatin1Char('_') + notification.methodName;
}
QString SqlGenerator::fixupComment(const QString& comment)
{
QString ret = comment;
ret = ret.remove(QLatin1Char('\''));
ret = ret.replace(QLatin1Char(';'), QLatin1Char(','));
return ret;
}
QStringList SqlGenerator::getDbSettings(const QString& dbSchema) const
{
QStringList data;
QString dbUser(QStringLiteral("ets_"));
if (m_exerciseId > 0)
dbUser += QStringLiteral("ex_00");
else
dbUser += m_xmlReader->getServiceAbbr().toLower();
data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') +
QLatin1String(" GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE ON TABLES TO ") + dbUser +
QLatin1Char(';'));
data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') +
QLatin1String(" GRANT SELECT, REFERENCES ON TABLES TO ets_ro;"));
data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') +
QLatin1String(" GRANT EXECUTE ON FUNCTIONS TO ") + dbUser + QLatin1Char(';'));
data.append(QLatin1String("ALTER DEFAULT PRIVILEGES IN SCHEMA ") + dbSchema + QLatin1Char('\n') +
QLatin1String(" GRANT ALL ON SEQUENCES TO ") + dbUser + QLatin1Char(';'));
data.append(QLatin1String("SET SEARCH_PATH TO ") + dbSchema + QLatin1String(", PUBLIC;"));
QStringList deps;
deps.reserve(m_referencedSevices.size());
for (const QString& svc : m_referencedSevices)
deps += QStringLiteral("-- DEPENDS: ") + svc;
return deps + data;
}
QStringList SqlGenerator::getAllSqlStmts(const QString& dbSchema, ExerciseDependency ed) const
{
DEBUG_LOG(m_logger) << "Creating statements for service " << getServiceAbbr() << " in exercise " << m_exerciseId
<< ", this: " << this;
QStringList data;
const SqlStatements& sql = getStatements(ed);
if (sql.isEmpty())
return data;
if (!dbSchema.isEmpty())
data << getDbSettings(dbSchema);
data.append(QStringLiteral("\n-- Drop tables/views/sequences/constraints"));
data.append(sql.dropViews);
data.append(sql.dropConstraints);
data.append(sql.dropTables);
data.append(sql.dropSequences);
data.append(QStringLiteral("\n-- Create tables"));
data.append(sql.createTables);
data.append(QStringLiteral("\n-- Create sequence(s)"));
data.append(sql.createSequences);
data.append(QStringLiteral("\n-- Create primary keys"));
data.append(sql.createPrimaryKeys);
data.append(QStringLiteral("\n-- Create foreign keys"));
data.append(sql.createForeignKeys);
data.append(QStringLiteral("\n-- Create views"));
data.append(sql.createViews);
data.append(
QStringLiteral("\n-- --------------------------------------------------------------------------------"));
data.append(QStringLiteral("\n-- Inserts for static data i.e. enums\n"));
data.append(sql.enumerationValues);
data.append(
QStringLiteral("\n-- --------------------------------------------------------------------------------"));
data.append(QStringLiteral("\n-- Notify functions for callbacks (signals in db interface)\n"));
data.append(sql.dbFunctions);
data.append(
QStringLiteral("\n-- --------------------------------------------------------------------------------"));
data.append(QStringLiteral("\n-- Notify function trigger for callbacks (signals in db interface)\n"));
data.append(sql.dbFunctionTrigger);
data.append(
QStringLiteral("\n-- --------------------------------------------------------------------------------"));
data.append(QStringLiteral("\n-- Indexes \n"));
data.append(sql.createConstraints);
return data;
}
QStringList SqlGenerator::getEnumerationInsertValues(const QString& table, const QString& dbSchema) const
{
const QVector<EnumInformation> enumerations = m_xmlReader->getEnumerations();
for (const EnumInformation& ei : enumerations)
{
if (ei.tableName == table)
return handleEnumeration(ei, dbSchema);
}
return QStringList();
}
bool SqlGenerator::writeSqlToFile(const QString& dbSchema, const QString& sqlFile, ExerciseDependency ed) const
{
return writeFile(sqlFile, getAllSqlStmts(dbSchema, ed).join(QLatin1Char('\n')));
}
bool SqlGenerator::writeSql(const QString& filePath, ExerciseDependency ed)
{
Q_ASSERT(ed == Global || ed == Exercise);
QString sqlFile = filePath + getServiceAbbr();
if (ed == ExerciseDependency::Exercise)
sqlFile += QLatin1Char('_') + getExerciseSchemaName(m_exerciseId);
sqlFile += QLatin1String(".sql");
const SqlStatements& sql = getStatements(ed);
if (sql.isEmpty())
{
DEBUG_LOG(m_logger) << "Sql file is emtpy: " << sqlFile;
return writeFile(sqlFile, QString());
}
const QString dbSchema =
(ed == ExerciseDependency::Exercise ? getExerciseSchemaName(m_exerciseId) : getServiceAbbr());
INFO_LOG(m_logger) << "Creating sql-stmt for schema (" << dbSchema << ")";
return writeSqlToFile(dbSchema, sqlFile, ed);
}
template <typename T>
static void addInternal(QStringList& global, QStringList& exercise, const T& stmt, ExerciseDependency ed)
{
switch (ed)
{
case ExerciseDependency::Global:
global += stmt;
break;
case ExerciseDependency::Exercise:
exercise += stmt;
break;
case ExerciseDependency::GlobalAndExercise:
global += stmt;
exercise += stmt;
break;
case ExerciseDependency::Undefined:
break;
}
}
void SqlGenerator::addToDropSequences(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.dropSequences, m_sqlExercise.dropSequences, stmt, ed);
}
void SqlGenerator::addToDropTables(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.dropTables, m_sqlExercise.dropTables, stmt, ed);
}
void SqlGenerator::addToDropViews(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.dropViews, m_sqlExercise.dropViews, stmt, ed);
}
void SqlGenerator::addToCreateConstraints(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.createConstraints, m_sqlExercise.createConstraints, stmt, ed);
}
void SqlGenerator::addToDropConstraints(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.dropConstraints, m_sqlExercise.dropConstraints, stmt, ed);
}
void SqlGenerator::addToCreateFks(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.createForeignKeys, m_sqlExercise.createForeignKeys, stmt, ed);
}
void SqlGenerator::addToCreatePks(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.createPrimaryKeys, m_sqlExercise.createPrimaryKeys, stmt, ed);
}
void SqlGenerator::addToCreateSequences(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.createSequences, m_sqlExercise.createSequences, stmt, ed);
}
void SqlGenerator::addToCreateTables(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.createTables, m_sqlExercise.createTables, stmt, ed);
}
void SqlGenerator::addToCreateTables(const QStringList& stmts, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.createTables, m_sqlExercise.createTables, stmts, ed);
}
void SqlGenerator::addToCreateViews(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.createViews, m_sqlExercise.createViews, stmt, ed);
}
void SqlGenerator::addToDbFunctions(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.dbFunctions, m_sqlExercise.dbFunctions, stmt, ed);
}
void SqlGenerator::addToDbFunctionTrigger(const QString& stmt, ExerciseDependency ed)
{
addInternal(m_sqlGlobal.dbFunctionTrigger, m_sqlExercise.dbFunctionTrigger, stmt, ed);
}
void SqlGenerator::addToConstraints(const QString& name, ExerciseDependency ed)
{
addInternal(m_sqlItemsGlobal.constraints, m_sqlItemsExercise.constraints, name, ed);
}
void SqlGenerator::addToFks(const QString& name, ExerciseDependency ed)
{
addInternal(m_sqlItemsGlobal.foreignKeys, m_sqlItemsExercise.foreignKeys, name, ed);
}
void SqlGenerator::addToPks(const QString& name, ExerciseDependency ed)
{
addInternal(m_sqlItemsGlobal.primaryKeys, m_sqlItemsExercise.primaryKeys, name, ed);
}
void SqlGenerator::addToSequences(const QString& name, ExerciseDependency ed)
{
addInternal(m_sqlItemsGlobal.sequences, m_sqlItemsExercise.sequences, name, ed);
}
void SqlGenerator::addToTables(const QString& name, ExerciseDependency ed)
{
addInternal(m_sqlItemsGlobal.tables, m_sqlItemsExercise.tables, name, ed);
}
bool SqlGenerator::writeFile(const QString& fn, const QString& content) const
{
QFile f(fn);
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
ERROR_LOG(m_logger) << "Error opening file '" << f.fileName() << "' for writing: " << f.errorString();
return false;
}
f.write(content.toUtf8());
if (!content.isEmpty())
f.write("\n");
f.close();
return true;
}
@@ -1,198 +0,0 @@
#pragma once
#include "StructBuilder.h"
#include "codegen_base_export.h"
#include <QtCore/QSharedPointer>
#include <QtCore/QStringList>
namespace NGL
{
class INGLLogger;
}
namespace CodeGen
{
struct ColumnInformation;
struct ConstraintInformation;
struct EnumInformation;
struct ForeignKeyInformation;
struct TableInformation;
class XmlReader;
class CODEGEN_BASE_EXPORT SqlGenerator final
{
public:
struct SqlStatements
{
QStringList dropViews;
QStringList dropTables;
QStringList dropSequences;
QStringList dropConstraints;
QStringList createTables;
QStringList createSequences;
QStringList createPrimaryKeys;
QStringList createForeignKeys;
QStringList createViews;
QStringList createConstraints;
QStringList enumerationValues;
QStringList dbFunctions;
QStringList dbFunctionTrigger;
bool isEmpty() const
{
return dropViews.isEmpty() && dropTables.isEmpty() && dropSequences.isEmpty() && dropConstraints.isEmpty();
}
};
struct SqlItems
{
QStringList tables;
QStringList sequences;
QStringList primaryKeys;
QStringList foreignKeys;
QStringList constraints;
};
public:
Q_DISABLE_COPY(SqlGenerator)
explicit SqlGenerator(XmlReader* xmlReader, StructBuilder* structBuilder);
explicit SqlGenerator(const QString& xmlFile);
~SqlGenerator();
bool isValid() const
{
return m_isValid;
}
bool generateData(int exerciseId);
bool createStatementsForTable(const QString& tableName, const QString& tableNameOut);
SqlStatements getStatements(ExerciseDependency ed) const
{
return (ed == ExerciseDependency::Exercise ? m_sqlExercise : m_sqlGlobal);
}
SqlItems getSqlItems(ExerciseDependency ed) const
{
return (ed == ExerciseDependency::Exercise ? m_sqlItemsExercise : m_sqlItemsGlobal);
}
QStringList getEnumerationInsertValues(const QString& table, const QString& dbSchema) const;
QStringList getReferencedServices() const
{
return m_referencedSevices;
}
QStringList getDbSettings(const QString& dbSchema) const;
bool writeSql(const QString& filePath, ExerciseDependency ed);
bool writeSqlToFile(const QString& dbSchema, const QString& sqlFile, ExerciseDependency ed) const;
QString getServiceAbbr() const
{
return m_xmlReader->getServiceAbbr();
}
bool isServiceExerciseIndependent() const
{
return m_xmlReader->isServiceExerciseIndependent();
}
bool isServiceGlobal() const
{
return (m_xmlReader->defaultExerciseDependency() == CodeGen::ExerciseDependency::Global ||
m_xmlReader->defaultExerciseDependency() == CodeGen::ExerciseDependency::GlobalAndExercise);
}
CodeGen::ExerciseDependency getExerciseDependency() const
{
return m_xmlReader->defaultExerciseDependency();
}
QStringList getGroupNames() const
{
return m_xmlReader->getGroupNames();
}
QStringList getAllSqlStmts(const QString& dbSchema, ExerciseDependency ed) const;
protected:
NGL::INGLLogger& m_logger;
private:
bool handleTables();
bool handleViews();
bool handleTable(const CompleteTableInformation& cti);
bool handleView(const ViewInformation& vi);
QString getTableColumnCreateStatements(const QString& tableName, const QVector<MemberInformation>& columns,
QStringList* tableComments, ExerciseDependency ed);
QString generateSequence(const QString& tableName, const FullQualifiedColumnInformation& ci, ExerciseDependency ed);
void generatePrimaryKey(const QString& tableName, const FullQualifiedColumnInformation& ci, ExerciseDependency ed);
void generateForeignKeys(const QVector<QSharedPointer<ForeignKeyInformation>>& foreignKeys, ExerciseDependency ed);
void generateForeignKey(const QSharedPointer<ForeignKeyInformation>& foreignKey, ExerciseDependency ed);
void generateConstraints(const QString& tableName, const QVector<ConstraintInformation>& constraints,
ExerciseDependency ed);
void generateConstraint(const QString& tableName, const ConstraintInformation& constraint, ExerciseDependency ed);
bool handleEnumerations();
bool handleEnumeration(const EnumInformation& ei);
QStringList handleEnumeration(const EnumInformation& ei, const QString& dbSchema) const;
bool generateDbNotifications(const QString& tableName, const QVector<DbNotifcationInformation>& notifications,
ExerciseDependency ed);
void generateDbNotification(const DbNotifcationInformation& notification, ExerciseDependency ed);
bool generateDbNotificationTrigger(const QString& tableName, const DbNotifcationInformation& notification,
ExerciseDependency ed);
QString getNotificationFunctionName(const DbNotifcationInformation& notification) const;
static QString fixupComment(const QString& comment);
private:
XmlReader* m_xmlReader;
StructBuilder* m_structBuilder;
bool m_createdByXmlFile;
void addToDropSequences(const QString& stmt, ExerciseDependency ed);
void addToDropTables(const QString& stmt, ExerciseDependency ed);
void addToDropViews(const QString& stmt, ExerciseDependency ed);
void addToDropConstraints(const QString& stmt, ExerciseDependency ed);
void addToCreateConstraints(const QString& stmt, ExerciseDependency ed);
void addToCreateFks(const QString& stmt, ExerciseDependency ed);
void addToCreatePks(const QString& stmt, ExerciseDependency ed);
void addToCreateSequences(const QString& stmt, ExerciseDependency ed);
void addToCreateTables(const QString& stmt, ExerciseDependency ed);
void addToCreateTables(const QStringList& stmts, ExerciseDependency ed);
void addToCreateViews(const QString& stmt, ExerciseDependency ed);
void addToDbFunctions(const QString& stmt, ExerciseDependency ed);
void addToDbFunctionTrigger(const QString& stmt, ExerciseDependency ed);
void addToConstraints(const QString& name, ExerciseDependency ed);
void addToFks(const QString& name, ExerciseDependency ed);
void addToPks(const QString& name, ExerciseDependency ed);
void addToSequences(const QString& name, ExerciseDependency ed);
void addToTables(const QString& name, ExerciseDependency ed);
bool writeFile(const QString& fn, const QString& content) const;
SqlStatements m_sqlGlobal;
SqlStatements m_sqlExercise;
SqlItems m_sqlItemsGlobal;
SqlItems m_sqlItemsExercise;
QStringList m_referencedSevices;
bool m_isValid;
int m_exerciseId;
};
} // namespace CodeGen
File diff suppressed because it is too large. Load diff
@@ -1,385 +0,0 @@
#pragma once
#include "XmlReader.h"
#include "codegen_base_export.h"
#include <QtCore/QString>
namespace NGL
{
class INGLLogger;
}
namespace CodeGen
{
class StructBuilder;
struct EaData
{
QString table;
QString column;
QString type;
QString toString() const
{
return table + QLatin1Char(';') + column + QLatin1Char(';') + type;
}
};
struct FullQualifiedColumnInformation : public ColumnInformation
{
QString table;
bool usedForInsert = false;
bool usedForView = false;
QString columnNameView;
QString getColumnNameView() const
{
return columnNameView.isEmpty() ? columnName : columnNameView;
}
FullQualifiedColumnInformation() = default;
FullQualifiedColumnInformation(const ColumnInformation& ci, const QString& tableName)
: ColumnInformation(ci), table(tableName)
{
}
QString toString() const
{
return table + QLatin1Char(';') + columnName + QLatin1Char(';') + datatype +
QLatin1Char(';') + (isPrimaryKey ? QLatin1String("true") : QLatin1String("false")) + QLatin1Char(';') +
(isForeignKey ? QLatin1String("true") : QLatin1String("false")) + QLatin1Char(';') +
(usedForInsert ? QLatin1String("true") : QLatin1String("false")) + QLatin1Char(';') +
(usedForView ? QLatin1String("true") : QLatin1String("false"));
}
};
class DataTypeInfo
{
public:
DataTypeInfo(const QString& name, bool isPod) : m_name(name), m_isPod(isPod) {}
virtual ~DataTypeInfo() = default;
virtual QString name() const
{
return m_name;
}
virtual QString idlType() const = 0;
virtual QString ddsType() const = 0;
virtual QString sqlType() const = 0;
virtual QString cppType() const = 0;
virtual QString cppTypeConstRef() const
{
return isPod() ? cppType() : QLatin1String("const ") + cppType() + QLatin1String(" &");
}
virtual bool isPod() const
{
return m_isPod;
}
virtual bool isSequence() const
{
return false;
}
virtual bool isStructure() const
{
return false;
}
virtual QVector<QSharedPointer<DataTypeInfo>> member() const
{
return QVector<QSharedPointer<DataTypeInfo>>();
}
virtual QSharedPointer<DataTypeInfo> innerDataType() const
{
return QSharedPointer<DataTypeInfo>();
}
static QSharedPointer<DataTypeInfo> fromIdlDataType(const QString& name, const QString& idlType,
StructBuilder* structBuilder,
const QString& svcName = QString());
protected:
const QString m_name;
const bool m_isPod;
};
struct IdlData : public IdlColumnInformation
{
QSharedPointer<DataTypeInfo> dataTypeInfo;
IdlData() = default;
explicit IdlData(const IdlColumnInformation& ci) : IdlColumnInformation(ci) {}
QString toString() const
{
return name + QLatin1Char(';') + type +
QLatin1Char(';') + (isKey ? QLatin1String("true") : QLatin1String("false")) + QLatin1Char(';') +
(isOptional ? QLatin1String("true") : QLatin1String("false"));
}
};
struct CppData
{
QString structName;
QString memberName;
QString defaultValue;
bool usedForCppStruct = false;
bool usedForJson = false;
bool isReadOnly = false;
bool isOptional = false;
QString type;
QString ddsType;
QString toVariantPre;
QString toVariantPost;
QString fromVariantPre1;
QString fromVariantPre2;
QString fromVariantPost;
QString toJsonPre;
QString toJsonPost;
QString fromJsonPre2;
QString fromJsonPost;
QString toString() const
{
return structName + QLatin1Char(';') + memberName + QLatin1Char(';') +
(usedForCppStruct ? QLatin1String("true") : QLatin1String("false")) + QLatin1Char(';') +
(isReadOnly ? QLatin1String("true") : QLatin1String("false")) + QLatin1Char(';') + type +
QLatin1Char(';') + toVariantPre + QLatin1Char(';') + toVariantPost + QLatin1Char(';') + fromVariantPre1 +
QLatin1Char(';') + fromVariantPre2 + QLatin1Char(';') + fromVariantPost;
}
};
struct MemberInformation
{
EaData ea;
FullQualifiedColumnInformation db;
CppData cpp;
bool isDdsKey = false;
QSharedPointer<ForeignKeyInformation> fk;
QSharedPointer<EnumInformation> enumeration;
QString toString() const
{
return ea.toString() + QLatin1Char(';') + db.toString() + QLatin1Char(';') + cpp.toString();
}
bool isEmpty() const
{
return ea.column.isEmpty();
}
};
struct CompleteStructInformation
{
QString structName;
QString viewName;
QSet<QSharedPointer<TableInformation>> tables;
QSharedPointer<TableInformation> primaryTable;
QVector<MemberInformation> member;
QVector<DbNotifcationInformation> dbNotifications;
bool hasStaticContent = false;
bool isEmpty() const
{
return primaryTable.isNull();
}
};
struct CompleteTopicTypeInformation final : public TopicTypeInformationBase
{
QString serviceName;
QVector<IdlData> members;
CompleteTopicTypeInformation() = default;
explicit CompleteTopicTypeInformation(const TopicTypeInformationBase& s) : TopicTypeInformationBase(s) {}
bool hasBaseValue() const
{
return !baseValueType.isEmpty();
}
bool isValid() const
{
return TopicTypeInformationBase::isValid() && !members.isEmpty();
}
};
struct CompleteTableInformation : public TableInformation
{
QVector<MemberInformation> columns;
explicit CompleteTableInformation(const TableInformation& o) : TableInformation(o) {}
CompleteTableInformation() = default;
bool isEmpty() const
{
return columns.isEmpty();
}
};
class CODEGEN_BASE_EXPORT StructBuilder
{
struct ColumnInformationEx : public ColumnInformation
{
QSharedPointer<TableInformation> table;
ColumnInformationEx() = default;
ColumnInformationEx(const ColumnInformation& o, const QSharedPointer<TableInformation>& ti)
: ColumnInformation(o), table(ti)
{
}
};
public:
struct ServiceTypeStruct
{
QString service;
QString type;
bool bIsSequence = false;
bool isExternalType() const
{
return !service.isEmpty();
}
bool isSequence() const
{
return bIsSequence;
}
QString fqType() const
{
return service + QStringLiteral("::") + type;
}
};
explicit StructBuilder(XmlReader* xmlReader);
~StructBuilder() = default;
bool buildStructures();
QVector<CompleteStructInformation> structInformations() const
{
return m_structInfos;
}
QVector<CompleteTopicTypeInformation> topicTypeInformations() const
{
return m_topicTypeInfos;
}
CompleteStructInformation structInformationByTableName(const QString& tableName) const;
EnumInformation enumInformationByType(const QString& type, bool checkExternals = true, bool verbose = true);
CompleteTopicTypeInformation topicTypeInformationByStructType(const QString& type, bool checkExternals = true);
CompleteTopicTypeInformation externalTopicTypeByStructType(const ServiceTypeStruct& sts);
QVector<EnumInformation> enumInformations() const
{
return m_enumInfos;
}
QMap<QString, CompleteTableInformation> getDbTables() const
{
return m_sqlTableInformations;
}
QMap<QString, ViewInformation> getDbViews() const
{
return m_sqlViewInformations;
}
bool needPoint3d() const
{
return m_needPoint3d;
}
bool needPgArrayInclude() const
{
return m_needPgArrayInclude;
}
CompleteTableInformation completeTableInformation(const CompleteTopicTypeInformation& ctti);
CompleteTableInformation getRecordingTopicTable(const QString& structName);
QMap<QString, CompleteTableInformation> getRecordingTopicTables();
QVector<QSharedPointer<DataTypeInfo>> getSerializeFromToJsonTopics() const;
bool fixupQVariantConversions(MemberInformation& mi) const;
Q_REQUIRED_RESULT static QString firstCharLowercase(const QString& txt);
Q_REQUIRED_RESULT static QString camelCaseString(const QString& in, bool bFirstUppercase = false);
Q_REQUIRED_RESULT static QString camelCaseToUnderline(const QString& in);
bool buildJSonStructureInformationFromIdlData(const CompleteTopicTypeInformation& ctti);
QMap<QString, QVector<MemberInformation>> jsonStructureInformation() const
{
return m_jsonStructureInformation;
}
private:
bool handleEnum(const EnumInformation& ei);
bool handleTable(const ServiceStructInformation& ssi);
bool handleIdlStructInfo(const TopicTypeInformation& tti, QVector<CompleteTopicTypeInformation>& destination,
const QString& svcName = QString());
bool loadExternalTopicType(const ServiceTypeStruct& sts);
bool getColumnsFromTable(const QString& tableName, QVector<ColumnInformationEx>& columns,
QVector<QString>& tableNames) const;
bool fixupCustomDbDatatypes(MemberInformation& memberInfo, QVector<MemberInformation>& members);
bool fixupDatatypePoint2d(MemberInformation& memberInfo, QVector<MemberInformation>& members);
bool fixupDatatypePoint3d(MemberInformation& memberInfo, QVector<MemberInformation>& members);
bool fixupDatatypeIntegerArray(MemberInformation& memberInfo);
QVector<MemberInformation> buildDbTableInformationFromIdlData(const CompleteTopicTypeInformation& ctti,
const QString& tableName,
const QString& columnNamePrefix,
const QString& cppStructure);
void addToSerializeFromToJsonTopics(const QSharedPointer<DataTypeInfo>& toAdd);
CompleteTableInformation getRecordingTopicTable(const CompleteTopicTypeInformation& ctti,
const MemberInformation& miPrimaryKey);
MemberInformation getRecordPrimaryKey() const;
ServiceTypeStruct extractServiceAndTypeFromTypename(const QString& type);
void addToSqlTableInformation(const MemberInformation& mi);
private:
QVector<CompleteStructInformation> m_structInfos;
QVector<CompleteTopicTypeInformation> m_topicTypeInfos;
mutable QVector<CompleteTopicTypeInformation> m_externalTopicTypes;
QVector<EnumInformation> m_enumInfos;
QMap<QString, CompleteTableInformation> m_sqlTableInformations;
QMap<QString, ViewInformation> m_sqlViewInformations;
QSet<QString> m_sqlTableInformationsAlreadyAvailable;
QVector<QSharedPointer<DataTypeInfo>> m_serializeFromToJsonTopics;
QMap<QString, QVector<MemberInformation>> m_jsonStructureInformation;
bool m_needPoint3d;
bool m_needPgArrayInclude;
XmlReader* m_xmlReader;
NGL::INGLLogger& m_logger;
NGL::INGLLogger& m_loggerCSV;
};
} // namespace CodeGen
@@ -1,893 +0,0 @@
#include "XmlReader.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QDirIterator>
#include <QtXml/QDomDocument>
#include <QtCore/QFile>
#include <QtCore/QXmlStreamReader>
#include <ngltools/ngltools_filesystem.h>
#include <utility>
using namespace CodeGen;
class XmlReaderException : public std::exception
{
public:
explicit XmlReaderException(const QString& msg) noexcept : m_msg(msg.toUtf8()) {}
~XmlReaderException() = default;
virtual const char* what() const noexcept
{
return m_msg.data();
}
private:
const QByteArray m_msg;
};
XmlReader::XmlReader() : m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.XmlReader")) {}
bool XmlReader::readServiceFile(const QString& svcFile, const QString& idlServiceInfoFile, const XmlReader* parent)
{
m_externalXmlInformations.clear();
m_idlFiles.clear();
m_xmlFiles.clear();
m_usedServices.clear();
m_fullServiceName2Abbreviation.clear();
m_svcNamespace.clear();
m_svcAbbr.clear();
m_svcProcNames.clear();
m_customRecordReplay = false;
m_exerciseDependency = ExerciseDependency::Exercise;
m_dbClassTables.clear();
m_struct2BaseClass.clear();
m_structsToGenerate.clear();
m_idlStructsToGenerate.clear();
m_tables.clear();
m_views.clear();
m_enumerations.clear();
m_functionTopics.clear();
m_externalTopicTypes.clear();
m_customFunctions.clear();
m_prio = 0;
m_svcDevMode = false;
if (parent)
{
m_idlFiles = parent->m_idlFiles;
m_xmlFiles = parent->m_xmlFiles;
m_fullServiceName2Abbreviation = parent->m_fullServiceName2Abbreviation;
m_externalXmlInformations = parent->m_externalXmlInformations;
}
loadIdlInfofile(idlServiceInfoFile);
QFile f(svcFile);
if (!f.exists())
{
ERROR_LOG(m_logger) << "Service file '" << svcFile << "' not found!";
return false;
}
if (!f.open(QIODevice::ReadOnly))
{
m_errorString =
QLatin1String("Can not open service file '") + svcFile + QLatin1String("' for reading: ") + f.errorString();
ERROR_LOG(m_logger) << m_errorString;
return false;
}
INFO_LOG(m_logger) << "SvcFile: " << svcFile;
QDomDocument doc;
const auto parseResult = doc.setContent(&f);
if (!parseResult)
{
m_errorString = QLatin1String("Error parsing service file '") + svcFile + QLatin1String("' at line ") +
QString::number(int(parseResult.errorLine)) + QLatin1Char(':') +
QString::number(int(parseResult.errorColumn));
ERROR_LOG(m_logger) << m_errorString;
return false;
}
QDomElement domRoot = doc.documentElement();
if (!domRoot.hasAttribute(QStringLiteral("namespace")))
{
m_errorString = QLatin1String("Xml file '") + svcFile +
QLatin1String("' is not a valid service file: service name/namespace is missing");
ERROR_LOG(m_logger) << m_errorString;
return false;
}
m_svcNamespace = getAttribute(domRoot, QStringLiteral("namespace"));
m_svcAbbr = getAttribute(domRoot, QStringLiteral("abbreviation")).toLower();
m_svcProcNames = getAttribute(domRoot, QStringLiteral("procNames")).split(QLatin1Char(','));
m_customRecordReplay = getAttributeBool(domRoot, QStringLiteral("customRecordReplay"), true, false);
QString devModeStr = getAttribute(domRoot, QStringLiteral("devMode"), true);
m_svcDevMode = devModeStr == QStringLiteral("1") || devModeStr == QStringLiteral("true");
INFO_LOG(m_logger) << "devMode: " << m_svcDevMode;
const QString groupNamesString = getAttribute(domRoot, QStringLiteral("groups"), true);
if (!groupNamesString.isEmpty())
{
m_groupNames = groupNamesString.split(QLatin1Char(','), Qt::SkipEmptyParts);
INFO_LOG(m_logger) << "groups: " << groupNamesString;
}
else
m_groupNames.clear();
m_exerciseDependency = string2ExerciseDependency(getAttribute(domRoot, QStringLiteral("exerciseDependency"), true));
if (m_groupNames.contains(QStringLiteral("Core")) || m_groupNames.contains(QStringLiteral("Master")))
{
if (m_exerciseDependency != ExerciseDependency::Global &&
m_exerciseDependency != ExerciseDependency::GlobalAndExercise)
{
WARNING_LOG(m_logger)
<< "Service " << getServiceNamespace()
<< " is not marked as exercise independent but should be started in group Core/Master";
}
}
INFO_LOG(m_logger) << "exerciseDependency: " << (int)m_exerciseDependency;
if (domRoot.hasAttribute(QStringLiteral("prio")))
{
m_prio = getAttribute(domRoot, QStringLiteral("prio"), 0).toInt();
}
try
{
parseDBSserviceStructs(domRoot.firstChildElement(QStringLiteral("dbServiceStructs")));
}
catch (const XmlReaderException& ex)
{
m_errorString = QLatin1String("Error parsing dbServiceStructs: ") + QLatin1String(ex.what());
ERROR_LOG(m_logger) << m_errorString;
return false;
}
try
{
parseDBTables(domRoot.firstChildElement(QStringLiteral("tables")));
}
catch (const XmlReaderException& ex)
{
m_errorString = QLatin1String("Error parsing dbtables: ") + QLatin1String(ex.what());
ERROR_LOG(m_logger) << m_errorString;
return false;
}
try
{
parseDBViews(domRoot.firstChildElement(QStringLiteral("views")));
}
catch (const XmlReaderException& ex)
{
m_errorString = QLatin1String("Error parsing dbviews: ") + QLatin1String(ex.what());
ERROR_LOG(m_logger) << m_errorString;
return false;
}
try
{
parseEnumerations(domRoot.firstChildElement(QStringLiteral("enumerations")));
}
catch (const XmlReaderException& ex)
{
m_errorString = QLatin1String("Error parsing enumerations: ") + QLatin1String(ex.what());
ERROR_LOG(m_logger) << m_errorString;
return false;
}
try
{
parseCustomDtcFunctions(domRoot.firstChildElement(QStringLiteral("customDtcFunctions")));
}
catch (const XmlReaderException& ex)
{
m_errorString = QLatin1String("Error parsing custom dtc functions: ") + QLatin1String(ex.what());
ERROR_LOG(m_logger) << m_errorString;
return false;
}
try
{
parseTopicTypes(domRoot.firstChildElement(QStringLiteral("topicTypes")));
}
catch (const XmlReaderException& ex)
{
m_errorString = QLatin1String("Error parsing idl strucs: ") + QLatin1String(ex.what());
ERROR_LOG(m_logger) << m_errorString;
return false;
}
try
{
parseFunctionTopics(domRoot.firstChildElement(QStringLiteral("functionTopics")));
}
catch (const XmlReaderException& ex)
{
m_errorString = QLatin1String("Error parsing function topics: ") + QLatin1String(ex.what());
ERROR_LOG(m_logger) << m_errorString;
return false;
}
return true;
}
QString XmlReader::getAttribute(const QDomElement& element, const QString& attributeName, bool bOptional)
{
if (!element.hasAttribute(attributeName))
{
if (bOptional)
return QString();
throw XmlReaderException(QLatin1String("Element ") + element.tagName() + QLatin1String(" has no attribute ") +
attributeName);
}
return element.attribute(attributeName);
}
bool XmlReader::getAttributeBool(const QDomElement& element, const QString& attributeName, bool bOptional,
bool defaultValue)
{
QString str = getAttribute(element, attributeName, bOptional);
if (str.isEmpty())
return defaultValue;
bool bOk;
bool ret = (str.toInt(&bOk) != 0);
if (!bOk)
throw XmlReaderException(QLatin1String("Can not parse boolean value: ") + str);
return ret;
}
int XmlReader::getAttributeInt(const QDomElement& element, const QString& attributeName, bool bOptional,
int defaultValue)
{
QString str = getAttribute(element, attributeName, bOptional);
if (str.isEmpty())
return defaultValue;
bool bOk;
int ret = str.toInt(&bOk);
if (!bOk)
{
ret = str.toUInt(&bOk, 16);
if (!bOk)
throw XmlReaderException(QLatin1String("Can not parse int value: ") + str);
}
return ret;
}
void XmlReader::parseDBSserviceStructs(const QDomElement& rootElement)
{
if (rootElement.isNull())
return;
QDomElement domDbStruct = rootElement.firstChildElement(QStringLiteral("serviceStruct"));
while (!domDbStruct.isNull())
{
ServiceStructInformation ssi;
ssi.name = getAttribute(domDbStruct, QStringLiteral("name"));
ssi.primaryTable = getAttribute(domDbStruct, QStringLiteral("table"));
ssi.hasStaticContent = getAttributeBool(domDbStruct, QStringLiteral("static"), true, false);
QDomElement domFunc = domDbStruct.firstChildElement(QStringLiteral("function"));
while (!domFunc.isNull())
{
m_customFunctions += parseCustomFunction(domFunc, CustomServiceFunctionInformation::Type::Db);
domFunc = domFunc.nextSiblingElement(domFunc.tagName());
}
QDomElement domNotify = domDbStruct.firstChildElement(QStringLiteral("notification"));
while (!domNotify.isNull())
{
ssi.dbNotifications.append(parseDbNotification(domNotify));
domNotify = domNotify.nextSiblingElement(domNotify.tagName());
}
m_structsToGenerate.push_back(ssi);
domDbStruct = domDbStruct.nextSiblingElement(domDbStruct.tagName());
}
std::sort(m_structsToGenerate.begin(), m_structsToGenerate.end());
domDbStruct = rootElement.firstChildElement(QStringLiteral("table"));
while (!domDbStruct.isNull())
{
const QString table = getAttribute(domDbStruct, QStringLiteral("name"));
const QString parentTable = getAttribute(domDbStruct, QStringLiteral("base"), true);
m_struct2BaseClass[table] = parentTable;
domDbStruct = domDbStruct.nextSiblingElement(domDbStruct.tagName());
}
INFO_LOG(m_logger) << "Read " << m_structsToGenerate.size() << " database service struct(s)";
}
void XmlReader::parseDBTables(const QDomElement& rootElement)
{
if (rootElement.isNull())
return;
QDomElement domDbTable = rootElement.firstChildElement(QStringLiteral("table"));
while (!domDbTable.isNull())
{
QSharedPointer<TableInformation> ti(new TableInformation);
ti->name = getAttribute(domDbTable, QStringLiteral("name"));
ti->structName = QLatin1String("t_") + ti->name;
ti->comment = getAttribute(domDbTable, QStringLiteral("comment"), true);
ti->exerciseDependency =
string2ExerciseDependency(getAttribute(domDbTable, QStringLiteral("exerciseDependency"), true));
QDomElement domColumn = domDbTable.firstChildElement(QStringLiteral("column"));
while (!domColumn.isNull())
{
ti->plainColumns.append(parseTableColumn(domColumn, ti->name));
domColumn = domColumn.nextSiblingElement(domColumn.tagName());
}
QDomElement domForeignKey = domDbTable.firstChildElement(QStringLiteral("foreignKey"));
while (!domForeignKey.isNull())
{
ti->foreignKeys.append(parseForeignKey(domForeignKey));
domForeignKey = domForeignKey.nextSiblingElement(domForeignKey.tagName());
}
QDomElement domConstraint = domDbTable.firstChildElement(QStringLiteral("constraint"));
while (!domConstraint.isNull())
{
ti->constraints.append(parseConstraint(domConstraint));
domConstraint = domConstraint.nextSiblingElement(domConstraint.tagName());
}
m_tables.insert(ti->name, ti);
domDbTable = domDbTable.nextSiblingElement(domDbTable.tagName());
}
INFO_LOG(m_logger) << "Read " << m_tables.size() << " database table(s)";
}
void XmlReader::parseDBViews(const QDomElement& rootElement)
{
if (rootElement.isNull())
return;
QDomElement domDbView = rootElement.firstChildElement(QStringLiteral("view"));
while (!domDbView.isNull())
{
QSharedPointer<ViewInformation> vi(new ViewInformation);
vi->name = getAttribute(domDbView, QStringLiteral("name"));
vi->structName = QLatin1String("v_") + vi->name;
vi->baseSchema = m_svcAbbr;
vi->baseTable = getAttribute(domDbView, QStringLiteral("base"));
vi->comment = getAttribute(domDbView, QStringLiteral("comment"), true);
vi->filter = getAttribute(domDbView, QStringLiteral("filter"));
vi->exerciseDependency =
string2ExerciseDependency(getAttribute(domDbView, QStringLiteral("exerciseDependency"), true));
DEBUG_LOG(m_logger) << "Base table = " << vi->baseTable;
m_views.insert(vi->name, vi);
domDbView = domDbView.nextSiblingElement(domDbView.tagName());
}
INFO_LOG(m_logger) << "Read " << m_views.size() << " database view(s)";
}
void XmlReader::parseEnumerations(const QDomElement& rootElement)
{
if (rootElement.isNull())
return;
QDomElement domEnum = rootElement.firstChildElement(QStringLiteral("enumeration"));
while (!domEnum.isNull())
{
EnumInformation enumeration;
enumeration.serviceName = getServiceNamespace();
enumeration.enumName = getAttribute(domEnum, QStringLiteral("name"));
enumeration.tableName = getAttribute(domEnum, QStringLiteral("table"), true);
QDomElement enumVal = domEnum.firstChildElement(QStringLiteral("attribute"));
while (!enumVal.isNull())
{
EnumKeyValuePair evp;
evp.key = getAttribute(enumVal, QStringLiteral("key"));
evp.value = getAttributeInt(enumVal, QStringLiteral("value"));
enumeration.values.append(evp);
enumVal = enumVal.nextSiblingElement(enumVal.tagName());
}
m_enumerations.push_back(enumeration);
domEnum = domEnum.nextSiblingElement(domEnum.tagName());
}
INFO_LOG(m_logger) << "Read " << m_enumerations.size() << " enumeration(s)";
}
void XmlReader::parseCustomDtcFunctions(const QDomElement& rootElement)
{
if (rootElement.isNull())
return;
QDomElement domElement = rootElement.firstChildElement(QStringLiteral("customDtcFunction"));
while (!domElement.isNull())
{
QDomElement domFunc = domElement.firstChildElement(QStringLiteral("function"));
while (!domFunc.isNull())
{
m_customFunctions += parseCustomFunction(domFunc, CustomServiceFunctionInformation::Type::Dtc);
domFunc = domFunc.nextSiblingElement(domFunc.tagName());
}
domElement = domElement.nextSiblingElement(domElement.tagName());
}
INFO_LOG(m_logger) << "Read " << m_enumerations.size() << " customDtcFunction(s)";
}
void XmlReader::parseTopicTypes(const QDomElement& rootElement)
{
if (rootElement.isNull())
return;
QDomElement domIdlStruct = rootElement.firstChildElement(QStringLiteral("topicType"));
while (!domIdlStruct.isNull())
{
TopicTypeInformation isi;
isi.name = getAttribute(domIdlStruct, QStringLiteral("name"));
isi.baseValueType = getAttribute(domIdlStruct, QStringLiteral("baseValueType"), true);
const QStringList sl = isi.baseValueType.split(QStringLiteral("::"));
if (sl.size() == 2)
loadExternalTopicType(sl.at(0), sl.at(1));
const QString tmp = getAttribute(domIdlStruct, QStringLiteral("extensibility"), true);
#ifdef RTI_HEADER_AVAILABLE
if (tmp.compare(QLatin1String("FINAL"), Qt::CaseInsensitive) == 0)
isi.extensibility = dds::core::xtypes::ExtensibilityKind::FINAL;
if (tmp.compare(QLatin1String("MUTABLE"), Qt::CaseInsensitive) == 0)
isi.extensibility = dds::core::xtypes::ExtensibilityKind::MUTABLE;
if (tmp.compare(QLatin1String("EXTENSIBLE"), Qt::CaseInsensitive) == 0)
isi.extensibility = dds::core::xtypes::ExtensibilityKind::EXTENSIBLE;
#else
// RTI not available - skip extensibility parsing
Q_UNUSED(tmp)
#endif
isi.comment = getAttribute(domIdlStruct, QStringLiteral("comment"), true);
isi.isDataObject = getAttributeBool(domIdlStruct, QStringLiteral("isDataObject"), true, false);
isi.dtsAutogen = getAttributeBool(domIdlStruct, QStringLiteral("autogenDTS"), true, true);
isi.dtcAutogen = getAttributeBool(domIdlStruct, QStringLiteral("autogenDTC"), true, true);
isi.recordTopic = getAttributeBool(domIdlStruct, QStringLiteral("recordTopic"), true, false);
isi.routeToClient = getAttributeBool(domIdlStruct, QStringLiteral("routeToClient"), true, true);
isi.routeToServer = getAttributeBool(domIdlStruct, QStringLiteral("routeToServer"), true, false);
isi.routeInReplay = getAttributeBool(domIdlStruct, QStringLiteral("routeInReplay"), true, false);
isi.autosubscribe = getAttributeBool(domIdlStruct, QStringLiteral("autosubscribe"), true, true);
QDomElement domColumn = domIdlStruct.firstChildElement(QStringLiteral("column"));
while (!domColumn.isNull())
{
isi.columns.append(parseIdlStructColumn(domColumn));
domColumn = domColumn.nextSiblingElement(domColumn.tagName());
}
m_idlStructsToGenerate.insert(isi.name, isi);
domIdlStruct = domIdlStruct.nextSiblingElement(domIdlStruct.tagName());
}
INFO_LOG(m_logger) << "Read " << m_idlStructsToGenerate.size() << " idl struct(s)";
}
void XmlReader::parseFunctionTopics(const QDomElement& rootElement)
{
if (rootElement.isNull())
return;
QDomElement domFunctionTopic = rootElement.firstChildElement(QStringLiteral("functionTopic"));
while (!domFunctionTopic.isNull())
{
FunctionTopic ft;
ft.name = getAttribute(domFunctionTopic, QStringLiteral("name"));
ft.requestTopicType = getAttribute(domFunctionTopic, QStringLiteral("requestTopicType"));
ft.replyTopicType = getAttribute(domFunctionTopic, QStringLiteral("replyTopicType"));
ft.requestTopicName = getAttribute(domFunctionTopic, QStringLiteral("requestTopicName"));
ft.replyTopicName = getAttribute(domFunctionTopic, QStringLiteral("replyTopicName"));
ft.routeInReplay = getAttributeBool(domFunctionTopic, QStringLiteral("routeInReplay"), true, false);
m_functionTopics.append(ft);
domFunctionTopic = domFunctionTopic.nextSiblingElement(domFunctionTopic.tagName());
}
for (const FunctionTopic& ft : std::as_const(m_functionTopics))
{
const QStringList sl1 = ft.requestTopicType.split(QStringLiteral("::"));
if (sl1.size() == 2)
loadExternalTopicType(sl1.at(0), sl1.at(1));
const QStringList sl2 = ft.replyTopicType.split(QStringLiteral("::"));
if (sl2.size() == 2)
loadExternalTopicType(sl2.at(0), sl2.at(1));
}
}
ColumnInformation XmlReader::parseTableColumn(const QDomElement& column, const QString& tableName) const
{
ColumnInformation ci;
ci.columnName = getAttribute(column, QStringLiteral("name"));
ci.datatype = getAttribute(column, QStringLiteral("datatype"));
ci.defaultValue = getAttribute(column, QStringLiteral("default"), true);
ci.comment = getAttribute(column, QStringLiteral("comment"), true);
ci.isPrimaryKey = getAttributeBool(column, QStringLiteral("isPrimaryKey"), true, false);
ci.isForeignKey = getAttributeBool(column, QStringLiteral("isForeignKey"), true, false);
ci.isOptional = getAttributeBool(column, QStringLiteral("isOptional"), true, false);
const QDomElement seq = column.firstChildElement(QStringLiteral("sequence"));
if (!seq.isNull())
{
ci.sequenceStart = getAttributeInt(seq, QStringLiteral("start"), true, -1);
ci.sequenceIncrement = getAttributeInt(seq, QStringLiteral("increment"), true, -1);
ci.hasSequence = ci.sequenceStart >= 0 && ci.sequenceIncrement > 0;
}
if (ci.columnName.toLower() != ci.columnName)
WARNING_LOG(m_logger) << "Column " << ci.columnName << " in Table " << tableName
<< " has has upper-case chars which is not allowed!";
return ci;
}
IdlColumnInformation XmlReader::parseIdlStructColumn(const QDomElement& column) const
{
IdlColumnInformation ci;
ci.name = getAttribute(column, QStringLiteral("name"));
ci.type = getAttribute(column, QStringLiteral("idlType"));
ci.comment = getAttribute(column, QStringLiteral("comment"), true);
ci.isKey = getAttributeBool(column, QStringLiteral("isKey"), true, false);
ci.isOptional = getAttributeBool(column, QStringLiteral("isOptional"), true, false);
ci.isTranslatable = getAttributeBool(column, QStringLiteral("isTranslatable"), true, false);
return ci;
}
QSharedPointer<ForeignKeyInformation> XmlReader::parseForeignKey(const QDomElement& foreignKey) const
{
QSharedPointer<ForeignKeyInformation> fk(new ForeignKeyInformation);
fk->name = getAttribute(foreignKey, QStringLiteral("name"));
const QString deleteAction = getAttribute(foreignKey, QStringLiteral("referential_integrity"), true);
if (deleteAction == QLatin1String("DeleteNoAction"))
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::NoAction;
else if (deleteAction == QLatin1String("DeleteCascade"))
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Cascade;
else if (deleteAction == QLatin1String("DeleteRestrict"))
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Restrict;
else if (deleteAction == QLatin1String("DeleteSetNull"))
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::SetNull;
else if (deleteAction == QLatin1String("DeleteSetDefault"))
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::SetDefault;
else
fk->deleteAction = ForeignKeyInformation::OnDeleteAction::Cascade;
fk->source = parseForeignKeyEndpoint(fk->name, foreignKey.firstChildElement(QStringLiteral("source")));
fk->destination = parseForeignKeyEndpoint(fk->name, foreignKey.firstChildElement(QStringLiteral("destination")));
return fk;
}
ForeignKeyInformation::Endpoint XmlReader::parseForeignKeyEndpoint(const QString& foreignKeyName,
const QDomElement& endpoint) const
{
if (endpoint.isNull())
throw XmlReaderException(QLatin1String("No endpoint for foreign key ") + foreignKeyName);
ForeignKeyInformation::Endpoint fkEndpoint;
fkEndpoint.service = getAttribute(endpoint, QStringLiteral("service"), true);
fkEndpoint.serviceAbbr = getAttribute(endpoint, QStringLiteral("serviceAbbr"), true);
fkEndpoint.schema = getAttribute(endpoint, QStringLiteral("schema"), true);
fkEndpoint.tableName = getAttribute(endpoint, QStringLiteral("table"));
fkEndpoint.columnName = getAttribute(endpoint, QStringLiteral("column"));
return fkEndpoint;
}
ConstraintInformation XmlReader::parseConstraint(const QDomElement& constraint) const
{
ConstraintInformation ret;
ret.name = getAttribute(constraint, QStringLiteral("name"), true);
const QString type = getAttribute(constraint, QStringLiteral("type")).toLower();
if (type == QLatin1String("unique"))
ret.type = ConstraintInformation::Type::unique;
else if (type == QLatin1String("index"))
ret.type = ConstraintInformation::Type::index;
QDomElement domColumn = constraint.firstChildElement(QStringLiteral("column"));
if (domColumn.isNull())
throw XmlReaderException(QLatin1String("No column for constraint") + ret.name);
while (!domColumn.isNull())
{
ret.affectedColumns += getAttribute(domColumn, QStringLiteral("name"));
domColumn = domColumn.nextSiblingElement(domColumn.tagName());
}
return ret;
}
CustomServiceFunctionInformation XmlReader::parseCustomFunction(const QDomElement& customFunc,
CustomServiceFunctionInformation::Type funcType) const
{
CustomServiceFunctionInformation ret;
ret.methodName = getAttribute(customFunc, QStringLiteral("name"));
ret.returnValue = getAttribute(customFunc, QStringLiteral("returns"), true);
ret.isConst = getAttributeBool(customFunc, QStringLiteral("isConst"), true, false);
if (ret.returnValue.isEmpty())
ret.returnValue = QStringLiteral("bool");
QMap<int, QPair<QString, QString>> paramHelper;
QDomElement domParam = customFunc.firstChildElement(QStringLiteral("parameter"));
while (!domParam.isNull())
{
int pos = getAttributeInt(domParam, QStringLiteral("pos"));
if (paramHelper.contains(pos))
throw XmlReaderException(QLatin1String("Doubled position entry for custom function ") + ret.methodName);
QString paramName = getAttribute(domParam, QStringLiteral("name"));
const QString paramType = getAttribute(domParam, QStringLiteral("type"));
const QString paramDefault = getAttribute(domParam, QStringLiteral("default"), true);
if (!paramDefault.isEmpty())
paramName.append(QLatin1String(" = ") + paramDefault);
paramHelper[pos] = qMakePair(paramName, paramType);
domParam = domParam.nextSiblingElement(domParam.tagName());
}
ret.paramNames.reserve(paramHelper.size());
ret.paramTypes.reserve(paramHelper.size());
ret.type = funcType;
QStringList signature;
for (auto it = paramHelper.constBegin(); it != paramHelper.constEnd(); ++it)
{
ret.paramNames += it.value().first;
ret.paramTypes += it.value().second;
QString tmp = it.value().second;
if (!it.value().second.endsWith(QLatin1Char('&')))
tmp += QLatin1Char(' ');
tmp += it.value().first;
signature += tmp;
}
ret.signature += signature.join(QStringLiteral(", "));
return ret;
}
DbNotifcationInformation XmlReader::parseDbNotification(const QDomElement& customFunc) const
{
DbNotifcationInformation ret(parseCustomFunction(customFunc, CustomServiceFunctionInformation::Type::DbNotify));
const QString attribute(getAttribute(customFunc, QStringLiteral("notificationTypes")).toLower());
const QStringList nfTypes = attribute.split(QLatin1Char(','));
static const QMap<QString, DbNotifcationInformation::NotificationType> s_nfTypeMap = {
{QLatin1String("beforeinsert"), DbNotifcationInformation::NotificationType::BeforeInsert},
{QLatin1String("afterinsert"), DbNotifcationInformation::NotificationType::AfterInsert},
{QLatin1String("beforeupdate"), DbNotifcationInformation::NotificationType::BeforeUpdate},
{QLatin1String("afterupdate"), DbNotifcationInformation::NotificationType::AfterUpdate},
{QLatin1String("beforedelete"), DbNotifcationInformation::NotificationType::BeforeDelete},
{QLatin1String("afterdelete"), DbNotifcationInformation::NotificationType::AfterDelete}};
for (const QString& nfType : nfTypes)
{
const auto it = s_nfTypeMap.find(nfType);
if (it == s_nfTypeMap.end())
throw XmlReaderException(QStringLiteral("Can not find notification type %2 in type map for function %1")
.arg(ret.methodName, nfType));
ret.notificationType += it.value();
}
if (ret.notificationType.isEmpty())
throw XmlReaderException(
QStringLiteral("Notification function '%1' without a valid notification type").arg(ret.methodName));
if (ret.paramTypes.size() != 1)
throw XmlReaderException(
QStringLiteral("Notification function '%1' must have exactly one parameter").arg(ret.methodName));
return ret;
}
TopicTypeInformation XmlReader::loadExternalTopicType(const QString& externalServiceName, const QString& topicTypeName)
{
DEBUG_LOG(m_logger) << "externalServiceName: " << externalServiceName << ", topicTypeName: " << topicTypeName;
if (!loadExternalService(externalServiceName))
{
WARNING_LOG(m_logger) << "Could not find service " << externalServiceName << " referenced by external topic "
<< topicTypeName;
return TopicTypeInformation();
}
auto extXmlReader = m_externalXmlInformations.value(externalServiceName);
if (!extXmlReader)
{
ERROR_LOG(m_logger) << "Could not find service xml file for service '" << externalServiceName << "'";
return TopicTypeInformation();
}
TopicTypeInformation tti = extXmlReader->getIdlStructInformation().value(topicTypeName);
if (tti.isValid())
{
tti.name = externalServiceName + QLatin1String("::") + tti.name;
m_externalTopicTypes += tti;
m_usedServices[externalServiceName] = true;
return tti;
}
WARNING_LOG(m_logger) << "Could not find topic " << topicTypeName << " in external service definition for service "
<< externalServiceName;
return TopicTypeInformation();
}
EnumInformation XmlReader::getExternalEnumeration(const QString& externalServiceName, const QString& enumName,
bool verbose)
{
DEBUG_LOG(m_logger) << "externalServiceName: " << externalServiceName << ", topicTypeName: " << enumName;
if (!loadExternalService(externalServiceName))
{
WARNING_LOG(m_logger) << "Could not find service '" << externalServiceName << "'"
<< " referenced by external enumeration " << enumName;
return EnumInformation();
}
auto extXmlReader = m_externalXmlInformations.value(externalServiceName);
if (!extXmlReader)
{
ERROR_LOG(m_logger) << "Could not find service xml file for service '" << externalServiceName << "'";
return EnumInformation();
}
const QVector<EnumInformation> enums = extXmlReader->getEnumerations();
for (const EnumInformation& ei : enums)
{
if (ei.enumName == enumName)
{
m_usedServices[externalServiceName] = true;
return ei;
}
}
if (verbose)
{
WARNING_LOG(m_logger) << "Could not find enumeration " << enumName << " in external service definition for '"
<< externalServiceName << "'";
}
return EnumInformation();
}
bool XmlReader::loadExternalService(const QString& serviceName)
{
if (serviceName == getServiceNamespace())
return false;
if (m_externalXmlInformations.contains(serviceName))
return true;
QString fileName = xmlFileForService(serviceName);
if (fileName.isEmpty())
{
const QVector<QString> dirs = NGL::directories(NGL::FileType::ConfigFile);
for (const QString& dirname : dirs)
{
QDirIterator dirIt(dirname + QStringLiteral("/xml"), QDir::Files | QDir::NoDotAndDotDot | QDir::Readable);
while (dirIt.hasNext())
{
dirIt.next();
if (!dirIt.fileName().endsWith(QLatin1String(".xml")))
continue;
fileName = dirIt.filePath();
const QString svcNamespace = serviceNamespaceFromFile(fileName);
if (svcNamespace == serviceName)
break;
}
}
}
if (fileName.isEmpty())
return false;
DEBUG_LOG(m_logger) << "xml file for " << serviceName << ": " << fileName;
auto extXmlReader = QSharedPointer<XmlReader>(new XmlReader);
if (!extXmlReader->readServiceFile(fileName, QString(), this))
{
WARNING_LOG(m_logger) << "Error reading service file " << fileName << " for service " << serviceName;
return false;
}
m_externalXmlInformations[serviceName] = extXmlReader;
if (!m_usedServices.contains(serviceName))
m_usedServices[serviceName] = false;
return true;
}
void XmlReader::loadIdlInfofile(const QString& idlServiceInfoFile)
{
if (idlServiceInfoFile.isEmpty())
return;
QFile f(idlServiceInfoFile);
if (!f.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Could not read idl service info file " << idlServiceInfoFile;
return;
}
const QList<QByteArray> files = f.readAll().split('\n');
for (const QByteArray& filePath : files)
{
if (filePath.startsWith("--") || filePath.trimmed().isEmpty())
continue;
const QString fileName = QString::fromLocal8Bit(filePath.trimmed());
const QStringList parts = fileName.split(QStringLiteral("/"));
getServiceNameInfoFromFilePath(parts);
const auto fn = parts.last().trimmed();
if (fn.endsWith(QLatin1String(".xml")))
{
const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).split(QLatin1Char('_')).at(0);
m_xmlFiles[svcAbbreveation] = fileName;
DEBUG_LOG(m_logger) << "xml: " << svcAbbreveation << " = " << fileName;
}
else
{
const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).toLower();
if (svcAbbreveation.endsWith(QLatin1String("_enums")))
continue;
m_idlFiles[svcAbbreveation] = fileName;
DEBUG_LOG(m_logger) << "idl: " << svcAbbreveation << " = " << fileName;
}
}
}
QString XmlReader::getIdlFileForService(const QMap<QString, QString>& data, const QString& serviceName) const
{
const QString serviceAbbr = serviceNameAbbreviation(serviceName);
return data.value(serviceAbbr);
}
QString XmlReader::serviceNameAbbreviation(const QString& serviceName) const
{
return m_fullServiceName2Abbreviation.value(serviceName);
}
void XmlReader::getServiceNameInfoFromFilePath(const QStringList& parts)
{
if (parts.size() < 2)
return;
const QString abbreviation = parts.last().split(QLatin1Char('.')).first().split(QLatin1Char('_')).first();
const QString serviceName = parts.at(parts.size() - 2);
DEBUG_LOG(m_logger) << "serviceName: " << serviceName << " = " << abbreviation;
m_fullServiceName2Abbreviation[serviceName] = abbreviation;
}
ExerciseDependency XmlReader::string2ExerciseDependency(const QString& str)
{
if (str.isEmpty())
return ExerciseDependency::Undefined;
const QStringList sd = str.toLower().split(QLatin1Char(','));
int ret = ExerciseDependency::Undefined;
for (const QString& s : sd)
{
if (s == QLatin1String("global"))
ret |= ExerciseDependency::Global;
else if (s == QLatin1String("exercise"))
ret |= ExerciseDependency::Exercise;
else if (s == QLatin1String("globalandexercise"))
ret |= ExerciseDependency::GlobalAndExercise;
}
return static_cast<ExerciseDependency>(ret);
}
QString XmlReader::serviceNamespaceFromFile(const QString& fn)
{
QFile f(fn);
if (!f.open(QIODevice::ReadOnly))
return QString();
QXmlStreamReader sr(&f);
while (!sr.atEnd())
{
sr.readNext();
if (sr.isStartElement() && sr.name() == QLatin1String("service"))
return (sr.attributes().value(QLatin1String("namespace"))).toString();
}
return QString();
}
@@ -1,459 +0,0 @@
#pragma once
#include <QtCore/QMap>
#include <QtCore/QSet>
#include <QtCore/QSharedPointer>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QStringView>
#include <QtCore/QVector>
#ifdef RTI_DDS_AVAILABLE
// Only include RTI headers if they are actually available
#if __has_include(<dds/core/xtypes/EnumType.hpp>)
#include <dds/core/xtypes/EnumType.hpp>
#define RTI_HEADER_AVAILABLE 1
#else
#undef RTI_DDS_AVAILABLE
#endif
#endif
#include "codegen_base_export.h"
namespace NGL
{
class INGLLogger;
}
class QDomElement;
namespace CodeGen
{
enum ExerciseDependency { Undefined = 0, Global = 0x1, Exercise = 0x2, GlobalAndExercise = 0x3 };
struct EnumKeyValuePair
{
QString key;
int value = 0;
};
struct EnumInformation
{
QString enumName;
QString tableName;
QString serviceName;
QVector<EnumKeyValuePair> values;
bool isValid() const
{
return !enumName.isEmpty();
}
};
struct ColumnInformation
{
QString columnName;
QString datatype;
QString comment;
QString defaultValue;
bool isPrimaryKey = false;
bool isAssignmentForPrimaryTablePk = false;
bool isForeignKey = false;
bool generateSelectRemove = false;
bool canBeNull = true;
bool hasSequence = false;
bool isOptional = false;
int sequenceStart = 0;
int sequenceIncrement = 0;
};
struct IdlColumnInformation
{
QString name;
QString type;
QString comment;
bool isKey = false;
bool isOptional = false;
bool isTranslatable = false;
};
struct ForeignKeyInformation
{
struct Endpoint
{
QString service;
QString serviceAbbr;
QString schema;
QString tableName;
QString columnName;
};
QString name;
Endpoint source;
Endpoint destination;
enum class OnDeleteAction {
NoAction,
Cascade,
Restrict,
SetNull,
SetDefault
} deleteAction = OnDeleteAction::Cascade;
};
struct CustomServiceFunctionInformation
{
QString methodName;
QString returnValue;
QString signature;
bool isConst = false;
QVector<QString> paramTypes;
QVector<QString> paramNames;
enum class Type { Db, DbNotify, Dtc } type = Type::Db;
};
struct DbNotifcationInformation : public CustomServiceFunctionInformation
{
DbNotifcationInformation() = default;
explicit DbNotifcationInformation(const CustomServiceFunctionInformation& d) : CustomServiceFunctionInformation(d)
{
}
enum class NotificationType { BeforeInsert, AfterInsert, BeforeUpdate, AfterUpdate, BeforeDelete, AfterDelete };
QVector<NotificationType> notificationType;
};
struct ConstraintInformation
{
enum struct Type { unknown = 0x00, unique = 0x01, index = 0x02 } type = Type::unknown;
QString name;
QStringList affectedColumns;
};
struct TableInformation
{
QString name;
QString structName;
QString comment;
QVector<ColumnInformation> plainColumns;
QVector<QSharedPointer<ForeignKeyInformation>> foreignKeys;
QVector<ConstraintInformation> constraints;
ExerciseDependency exerciseDependency = ExerciseDependency::Undefined;
QSharedPointer<ForeignKeyInformation> fkInformationForColumn(const QString& tableName,
const QString& columnName) const
{
for (const QSharedPointer<ForeignKeyInformation>& fki : foreignKeys)
{
if (fki->source.columnName == columnName && fki->source.tableName == tableName)
return fki;
}
return QSharedPointer<ForeignKeyInformation>();
}
};
struct ViewInformation
{
QString name;
QString comment;
QString structName;
QString baseTable;
QString baseSchema;
QString filter;
ExerciseDependency exerciseDependency = ExerciseDependency::Undefined;
};
struct ServiceStructInformation
{
QString name;
QString primaryTable;
bool hasStaticContent = false;
bool operator<(const ServiceStructInformation& o) const
{
if (hasStaticContent != o.hasStaticContent)
return hasStaticContent < o.hasStaticContent;
if (name != o.name)
return name < o.name;
return primaryTable < o.primaryTable;
}
QVector<DbNotifcationInformation> dbNotifications;
};
struct TopicTypeInformationBase
{
TopicTypeInformationBase() = default;
virtual ~TopicTypeInformationBase() = default;
QString name;
QString baseValueType;
QString comment;
bool isDataObject = false;
bool dtsAutogen = true;
bool dtcAutogen = true;
bool recordTopic = false;
bool routeToClient = true;
bool routeToServer = false;
bool routeInReplay = true;
bool autosubscribe = true;
#ifdef RTI_DDS_AVAILABLE
dds::core::xtypes::ExtensibilityKind extensibility = dds::core::xtypes::ExtensibilityKind::EXTENSIBLE;
#else
int extensibility = 0; // Placeholder when RTI DDS is not available
#endif
virtual bool isValid() const
{
return !name.isEmpty();
}
bool operator<(const TopicTypeInformationBase& o) const
{
if (name != o.name)
return name < o.name;
return baseValueType < o.baseValueType;
}
};
struct TopicTypeInformation : public TopicTypeInformationBase
{
QVector<IdlColumnInformation> columns;
};
struct FunctionTopic
{
QString name;
QString requestTopicType;
QString replyTopicType;
QString requestTopicName;
QString replyTopicName;
bool routeInReplay = false;
};
class CODEGEN_BASE_EXPORT XmlReader final
{
public:
XmlReader();
bool readServiceFile(const QString& svcFile, const QString& idlServiceInfoFile = QString(),
const XmlReader* parent = nullptr);
QString errorString() const
{
return m_errorString;
}
QString getServiceNamespace() const
{
return m_svcNamespace;
}
QString getServiceAbbr() const
{
return m_svcAbbr;
}
QStringList getGroupNames() const
{
return m_groupNames;
}
int getPrio() const
{
return m_prio;
}
QStringList getProcNames() const
{
return m_svcProcNames;
}
bool hasCustomRecordReplayPlugin() const
{
return m_customRecordReplay;
}
bool getDevMode() const
{
return m_svcDevMode;
}
bool isServiceExerciseIndependent() const
{
return m_exerciseDependency == ExerciseDependency::Global;
}
ExerciseDependency defaultExerciseDependency() const
{
return m_exerciseDependency;
}
QMap<QString, QVector<QString>> getDbClass2TableMapping() const
{
return m_dbClassTables;
}
QMap<QString, QString> getStruct2BaseClassRelations() const
{
return m_struct2BaseClass;
}
QVector<ServiceStructInformation> getStructToGenerate() const
{
return m_structsToGenerate;
}
QMap<QString, TopicTypeInformation> getIdlStructInformation() const
{
return m_idlStructsToGenerate;
}
QVector<TopicTypeInformation> getIdlStructToGenerate() const
{
QVector<TopicTypeInformation> res;
res.reserve(m_idlStructsToGenerate.size());
auto it = m_idlStructsToGenerate.constBegin();
while (it != m_idlStructsToGenerate.constEnd())
{
res.append(it.value());
++it;
}
return res;
}
QMap<QString, QSharedPointer<TableInformation>> getTables() const
{
return m_tables;
}
QMap<QString, QSharedPointer<ViewInformation>> getViews() const
{
return m_views;
}
QSharedPointer<TableInformation> getTable(const QString& table) const
{
return m_tables.value(table);
}
QVector<EnumInformation> getEnumerations() const
{
return m_enumerations;
}
QVector<IdlColumnInformation> getIdlColumns(const QString& structName) const
{
return m_idlStructsToGenerate.value(structName).columns;
}
QVector<FunctionTopic> getFunctionTopics() const
{
return m_functionTopics;
}
QVector<TopicTypeInformation> getExternalTopicTypes() const
{
return m_externalTopicTypes;
}
QString idlFileForService(const QString& serviceName) const
{
return getIdlFileForService(m_idlFiles, serviceName);
}
QString xmlFileForService(const QString& serviceName) const
{
return getIdlFileForService(m_xmlFiles, serviceName);
}
QMap<QString, bool> usedServices() const
{
return m_usedServices;
}
QVector<CustomServiceFunctionInformation> getCustomFunctions() const
{
return m_customFunctions;
}
TopicTypeInformation loadExternalTopicType(const QString& externalServiceName, const QString& topicTypeName);
EnumInformation getExternalEnumeration(const QString& externalServiceName, const QString& enumName, bool verbose);
static ExerciseDependency string2ExerciseDependency(const QString& str);
XmlReader(const XmlReader&) = delete;
XmlReader& operator=(const XmlReader&) = delete;
private:
NGL::INGLLogger& m_logger;
void parseDBSserviceStructs(const QDomElement& rootElement);
void parseDBTables(const QDomElement& rootElement);
void parseDBViews(const QDomElement& rootElement);
void parseEnumerations(const QDomElement& rootElement);
void parseTopicTypes(const QDomElement& rootElement);
void parseFunctionTopics(const QDomElement& rootElement);
void parseCustomDtcFunctions(const QDomElement& rootElement);
static QString getAttribute(const QDomElement& element, const QString& attributeName, bool bOptional = false);
static bool getAttributeBool(const QDomElement& element, const QString& attributeName, bool bOptional,
bool defaultValue);
static int getAttributeInt(const QDomElement& element, const QString& attributeName, bool bOptional = false,
int defaultValue = 0);
ColumnInformation parseTableColumn(const QDomElement& column, const QString& tableName) const;
IdlColumnInformation parseIdlStructColumn(const QDomElement& column) const;
QSharedPointer<ForeignKeyInformation> parseForeignKey(const QDomElement& foreignKey) const;
ForeignKeyInformation::Endpoint parseForeignKeyEndpoint(const QString& foreignKeyName,
const QDomElement& endpoint) const;
ConstraintInformation parseConstraint(const QDomElement& constraint) const;
CustomServiceFunctionInformation parseCustomFunction(const QDomElement& customFunc,
CustomServiceFunctionInformation::Type funcType) const;
DbNotifcationInformation parseDbNotification(const QDomElement& customFunc) const;
bool loadExternalService(const QString& serviceName);
void loadIdlInfofile(const QString& idlServiceInfoFile);
QString getIdlFileForService(const QMap<QString, QString>& data, const QString& serviceName) const;
QString serviceNameAbbreviation(const QString& serviceName) const;
void getServiceNameInfoFromFilePath(const QStringList& parts);
static QString serviceNamespaceFromFile(const QString& fn);
QString m_svcNamespace;
QString m_svcAbbr;
QStringList m_svcProcNames;
bool m_customRecordReplay;
ExerciseDependency m_exerciseDependency{ExerciseDependency::Exercise};
QMap<QString, QVector<QString>> m_dbClassTables;
QMap<QString, QString> m_struct2BaseClass;
QVector<ServiceStructInformation> m_structsToGenerate;
QMap<QString, TopicTypeInformation> m_idlStructsToGenerate;
QVector<FunctionTopic> m_functionTopics;
QVector<TopicTypeInformation> m_externalTopicTypes;
QVector<CustomServiceFunctionInformation> m_customFunctions;
QMap<QString, QSharedPointer<TableInformation>> m_tables;
QMap<QString, QSharedPointer<ViewInformation>> m_views;
QVector<EnumInformation> m_enumerations;
QString m_errorString;
QMap<QString, QSharedPointer<XmlReader>> m_externalXmlInformations;
QMap<QString, QString> m_idlFiles;
QMap<QString, QString> m_xmlFiles;
QMap<QString, bool> m_usedServices;
QMap<QString, QString> m_fullServiceName2Abbreviation;
QStringList m_groupNames;
int m_prio;
bool m_svcDevMode{false};
};
} // namespace CodeGen
@@ -1,17 +0,0 @@
#pragma once
#include <QtCore/qglobal.h>
#ifndef CODEGEN_BASE_EXPORT
#ifdef BUILD_STATIC_LIBRARIES
#define CODEGEN_BASE_EXPORT
#else
#if defined(MAKE_CODEGEN_BASE_LIB)
#define CODEGEN_BASE_EXPORT Q_DECL_EXPORT
#else
#define CODEGEN_BASE_EXPORT Q_DECL_IMPORT
#endif
#endif
#endif
@@ -1,101 +0,0 @@
log4j.reset=true
log4j.rootLogger=WARN, stdout
# CodeGeneration Tool
###########################################################
log4j.logger.CodeGen.main=WARN
log4j.logger.CodeGen=INFO
log4j.logger.CodeGen.Settings=INFO
log4j.logger.CodeGen.FtsImpl=INFO
log4j.logger.CodeGen.FtcImpl=INFO
# DTS/DTC, FTS/FTC
log4j.logger.Generic.DTS=INFO
log4j.logger.Generic.DTC=INFO
log4j.logger.Generic.FTS.Function=INFO
log4j.logger.Generic.FTC=INFO
log4j.logger.DTC.Subscriber=INFO
log4j.logger.DTC.SubscriberListener=INFO
log4j.logger.FTS=INFO
log4j.logger.FTC=WARN
# connext (request/reply feature)
log4j.logger.connext.requester=WARN
log4j.logger.connext.replier=WARN
# CodeGen
log4j.logger.CodeGen.XmlReader=WARN
# SqlGenerator
log4j.logger.CodeGen.SqlGenerator=WARN
log4j.logger.CodeGen.StructBuilder=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}_codeGen.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:
@@ -1,498 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function
import xml.etree.ElementTree as ET
import os
import re
import string
import sys
import time
class FtData(object):
def __init__( self ):
self.name = ''
self.namespace = ''
self.requestTopicType = ''
self.requestTopicName = ''
self.replyTopicType = ''
self.replyTopicName = ''
def fqnVar( self, name ):
if name.find('::') < 0:
name = self.namespace + '::' + name
return name
def stripNamespace( self, str ):
return str.split('::')[-1]
def getReplyTopicName( self ):
return 'LTS_' + self.stripNamespace( self.replyTopicName ).replace( '::', '_' )
def getReplyTopicNameType( self, withNamespace = True ):
var = self.replyTopicName
return self.fqnVar( var ) if withNamespace else self.stripNamespace( var )
def getRequestTopicName( self ):
return 'LTS_' + self.stripNamespace( self.requestTopicName ).replace( '::', '_' )
def getRequestTopicNameType( self, withNamespace = True ):
var = self.requestTopicName
return self.fqnVar( var ) if withNamespace else self.stripNamespace( var )
def getReplyTopicType( self ):
return self.fqnVar( self.replyTopicType )
def getRequestTopicType( self ):
return self.fqnVar( self.requestTopicType )
def needReplyTopicTypedef( self ):
return self.getReplyTopicNameType() != self.getReplyTopicType()
def needRequestTopicTypedef( self ):
return self.getRequestTopicNameType() != self.getRequestTopicType()
class GuiPluginRights(object):
def __init__( self ):
self.name = ''
self.offset = 0
self.groups = []
class GuiPluginRightsGroup(object):
def __init__( self ):
self.name = ''
self.desc = ''
self.values = []
class FileParser(object):
def __init__( self ):
self.filename = ''
self.service_abbr = ''
self.module_name = ''
self.struct_names = []
self.ft_enums = []
self.ft_replies = []
self.ft_data = dict()
self.op_right_offset = 0
self.op_rights = []
def build(self, path):
""" Parse the whole stuff ... """
self.filename = path
self.read_file_xml(self.filename)
def read_file_xml(self, filename):
""" Read the given xml service file"""
"""print >> sys.stdout, 'Reading xml file %s' % filename"""
xmlTree = ET.parse(filename)
root = xmlTree.getroot()
self.module_name = root.attrib['namespace']
self.service_abbr = root.attrib['abbreviation'].lower()
for child in root.iter('topicType'):
struct_name = child.attrib['name']
if 'isDataObject' in child.attrib and int(child.attrib['isDataObject']) != 0:
self.struct_names.append(struct_name)
for child in root.iter('functionTopic'):
ft_name = child.attrib['name']
self.ft_enums.append(ft_name)
ft_data = FtData()
ft_data.name = ft_name
ft_data.namespace = self.module_name
ft_data.requestTopicType = child.attrib['requestTopicType']
ft_data.requestTopicName = child.attrib['requestTopicName']
ft_data.replyTopicType = child.attrib['replyTopicType']
ft_data.replyTopicName = child.attrib['replyTopicName']
self.ft_data[ft_name] = ft_data
for child in root.iter('operatorRights'):
self.op_right_offset = int(child.attrib['offset'])
for pluginChild in child.iter('guiPlugin'):
r = GuiPluginRights()
r.name = pluginChild.attrib['name']
r.offset = int(pluginChild.attrib['offset'])
r.groups = [];
for groups in pluginChild.iter('operatorRightGroup'):
g = GuiPluginRightsGroup();
g.name = groups.attrib['name']
g.desc = groups.attrib['description'] if 'description' in groups.attrib else ''
for right in groups.iter('operatorRight'):
desc = right.attrib['description'] if 'description' in right.attrib else ''
g.values.append((right.attrib['right'], int(right.attrib['value']), desc))
r.groups.append(g)
self.op_rights.append(r)
def outputHeader( outfile, parser, description, with_dds_include = True, with_pragma = True ):
outfile.write('\n')
outfile.write('/**\n')
outfile.write('* %s definitions\n' % description)
outfile.write('*/\n')
outfile.write('\n')
outfile.write('// warning: this file is generated with command\n')
outfile.write('// %s\n' % ( " ".join(sys.argv) ))
outfile.write('// at %s\n' % time.strftime( '%c' ))
outfile.write('// it will be overwritten by next run - do not modify!\n')
outfile.write('\n')
if with_pragma:
outfile.write('#pragma once\n')
outfile.write('\n')
if with_dds_include:
outfile.write('#include "%s.hpp"\n' % ( parser.service_abbr ))
class DtsGenerator(object):
def generate( self, outfile, parser ):
outputHeader( outfile, parser, 'DTS' )
outfile.write('\n')
outfile.write('/**\n')
outfile.write(' * Type definitions \n')
outfile.write(' * => DataWriterObjects (DWOs)\n')
outfile.write(' * => DataTopicServers (DTSs)\n')
outfile.write(' */\n')
outfile.write('\n')
outfile.write('namespace DDSGeneric\n')
outfile.write('{\n')
outfile.write('template <typename T>\n')
outfile.write('class GenericDataWriter;\n')
outfile.write('}\n')
outfile.write('\n')
outfile.write('namespace DTS\n')
outfile.write('{\n')
outfile.write(' template<typename T> class GenericDts;\n')
outfile.write(' template<typename T> class GenericDtsImpl;\n')
outfile.write('\n')
for struct_name in sorted(parser.struct_names):
topic_name = "%s::%s" % ( parser.module_name, struct_name )
dwo_name = "Dwo%s" % ( struct_name )
outfile.write(' // Topic %s\n' % ( topic_name ))
outfile.write(' // Interface\n')
outfile.write(' using %s = GenericDts<%s>;\n' % ( struct_name, topic_name ))
outfile.write(' // Interface impl\n')
outfile.write(' using %s = DDSGeneric::GenericDataWriter<%s>;\n' % ( dwo_name, topic_name ))
outfile.write(' using %sImpl = GenericDtsImpl<%s>;\n' % ( struct_name, dwo_name ))
outfile.write('\n')
outfile.write('} // namespace DTS\n')
outfile.write('\n')
class DtcGenerator(object):
def generate( self, outfile, parser ):
outfile.write('\n')
outfile.write('/**\n')
outfile.write('* %s definitions\n' % 'DTC')
outfile.write('*/\n')
outfile.write('\n')
outfile.write('// warning: this file is generated with command\n')
outfile.write('// %s\n' % ( " ".join(sys.argv) ))
outfile.write('// at %s\n' % time.strftime( '%c' ))
outfile.write('// it will be overwritten by next run - do not modify!\n')
outfile.write('\n')
outfile.write('#pragma once\n')
outfile.write('\n')
# Include Qt headers FIRST to avoid namespace conflicts with DDS
outfile.write('#include <QtCore/QObject>\n')
outfile.write('#include <QtCore/QTimer>\n')
outfile.write('\n')
# Include DDS headers BEFORE project headers to avoid namespace conflicts
outfile.write('#include <DDS/Generic/generic_data_reader.h>\n')
outfile.write('#include <DDS/DTC/generic_dtc.h>\n')
outfile.write('#include <DDS/DTC/generic_dtc_impl.h>\n')
outfile.write('\n')
outfile.write('#include "%s.hpp"\n' % ( parser.service_abbr ))
outfile.write('\n')
outfile.write('/**\n')
outfile.write(' * Type definitions \n')
outfile.write(' * => DataTopicClients (DTCs)\n')
outfile.write(' */\n')
outfile.write('\n')
outfile.write('namespace DDSGeneric\n')
outfile.write('{\n')
outfile.write('template <typename T>\n')
outfile.write('class GenericDataReader;\n')
outfile.write('template <typename T>\n')
outfile.write('class GenericDataReaderContentFilter;\n')
outfile.write('class DynamicDataReader;\n')
outfile.write('}\n')
outfile.write('\n')
outfile.write('namespace DTC\n')
outfile.write('{\n')
outfile.write(' template<typename T> class GenericDtc;\n')
outfile.write(' template<typename T> class GenericDtcImpl;\n')
outfile.write(' template<typename T> class GenericDtcSampleInfo;\n')
outfile.write(' template<typename T> class GenericDtcImplSampleInfo;\n')
outfile.write('\n')
for struct_name in sorted(parser.struct_names):
topic_name = "%s::%s" % ( parser.module_name, struct_name )
dro_name = "Dro%s" % ( struct_name )
dro_name_cf = "Dro%sCF" % ( struct_name )
outfile.write(' // Topic %s\n' % ( topic_name ))
outfile.write(' using %s = DDSGeneric::GenericDataReader<%s>;\n' % ( dro_name, topic_name ))
outfile.write(' using %s = GenericDtc<%s>;\n' % ( struct_name, dro_name ))
outfile.write(' using %sImpl = GenericDtcImpl<%s>;\n' % ( struct_name, dro_name ))
outfile.write(' using %s = DDSGeneric::GenericDataReaderContentFilter<%s>;\n' % ( dro_name_cf, topic_name ))
outfile.write(' using %sCF = GenericDtc<%s>;\n' % ( struct_name, dro_name_cf ))
outfile.write(' using %sCFImpl = GenericDtcImpl<%s>;\n' % ( struct_name, dro_name_cf ))
outfile.write(' namespace SampleInfo {\n')
outfile.write(' using %s = GenericDtcSampleInfo<%s>;\n' % ( struct_name, dro_name ))
outfile.write(' using %sImpl = GenericDtcImplSampleInfo<%s>;\n' % ( struct_name, dro_name ))
outfile.write(' }\n')
outfile.write('\n')
outfile.write('} // namespace DTC\n')
outfile.write('\n')
def printFtTopicTypeNames( outfile, ft_data ):
outfile.write(' template<> struct TopicNames<%s>' % ft_data.name)
outfile.write(' {\n')
outfile.write(' static constexpr const char *functionTopicName() { return "%s"; }\n' % ft_data.name)
outfile.write(' static constexpr const char *replyTopicName() { return "%s"; }\n' % ft_data.getReplyTopicName())
outfile.write(' static constexpr const char *replyTopicTypeName() { return "%s"; }\n' % ft_data.getReplyTopicNameType( True ))
outfile.write(' static constexpr const char *requestTopicName() { return "%s"; }\n' % ft_data.getRequestTopicName())
outfile.write(' static constexpr const char *requestTopicTypeName() { return "%s"; }\n' % ft_data.getRequestTopicNameType( True ))
outfile.write(' };\n')
def printFtTypedefs( outfile, ft_data ):
if ft_data.needRequestTopicTypedef():
outfile.write(' using %s = %s;\n' % ( ft_data.getRequestTopicNameType( False ), ft_data.getRequestTopicType() ))
if ft_data.needReplyTopicTypedef():
outfile.write(' using %s = %s;\n' % ( ft_data.getReplyTopicNameType( False ), ft_data.getReplyTopicType() ))
class FtsGenerator(object):
def generate( self, outfile, parser):
outputHeader( outfile, parser, 'FTS' )
outfile.write('\n')
outfile.write('namespace FTS\n')
outfile.write('{\n')
outfile.write('template <typename REQ, typename REP, typename DUMMY> class GenericFtsImpl;\n')
outfile.write('template<typename GENERIC_FTS> struct TopicNames;\n')
outfile.write('}\n')
outfile.write('namespace %s\n' % ( parser.module_name ))
outfile.write('{\n')
outfile.write(' // TypeDefinitions for Replies that are not implemented => using Core::FunctionReply\n')
for ft_name in sorted(parser.ft_enums):
printFtTypedefs( outfile, parser.ft_data[ft_name] )
outfile.write('} // namespace %s\n' % ( parser.module_name ))
outfile.write('\n')
outfile.write('\n')
outfile.write('namespace FTS\n')
outfile.write('{\n')
outfile.write(' // TypeDefinitions for GenericFtsImpl\n')
for ft_name in sorted(parser.ft_enums):
ft_data = parser.ft_data[ft_name]
dummy = '%sTemplateHelperDummy' % ft_data.name;
outfile.write(' class %s;\n' % dummy)
outfile.write(' using %s = GenericFtsImpl<%s, %s, %s>;\n' % (ft_data.name, ft_data.getRequestTopicType(), ft_data.getReplyTopicNameType(), dummy))
printFtTopicTypeNames( outfile, ft_data )
outfile.write('\n')
outfile.write('} // namespace FTS\n')
outfile.write('\n')
class FtcGenerator(object):
def generate( self, outfile, parser ):
outputHeader( outfile, parser, 'FTC' )
outfile.write('\n')
outfile.write('namespace FTC\n')
outfile.write('{\n')
outfile.write('template <typename REQ, typename REP, typename DUMMY> class GenericFtcImpl;\n')
outfile.write('template<typename GENERIC_FTC> struct TopicNames;\n')
outfile.write('}\n')
outfile.write('namespace %s\n' % ( parser.module_name ))
outfile.write('{\n')
outfile.write(' // TypeDefinitions for Replies that are not implemented => using Core::FunctionReply\n')
for ft_name in sorted(parser.ft_enums):
printFtTypedefs( outfile, parser.ft_data[ft_name] )
outfile.write('} // namespace %s\n' % ( parser.module_name ))
outfile.write('\n')
outfile.write('\n')
outfile.write('namespace FTC\n')
outfile.write('{\n')
outfile.write(' // TypeDefinitions for GenericFtcImpl\n')
for ft_name in sorted(parser.ft_enums):
ft_data = parser.ft_data[ft_name]
dummy = '%sTemplateHelperDummy' % ft_data.name;
outfile.write(' class %s;\n' % dummy)
outfile.write(' using %s = GenericFtcImpl<%s, %s, %s>;\n' % (ft_data.name, ft_data.getRequestTopicType(), ft_data.getReplyTopicNameType(), dummy))
printFtTopicTypeNames( outfile, ft_data )
outfile.write('\n')
outfile.write('} // namespace FTC\n')
outfile.write('\n')
class OperatorRights(object):
def generate( self, outfile, parser ):
outputHeader( outfile, parser, 'Operator Rights', False )
outfile.write('namespace %s \n' % parser.module_name)
outfile.write('{\n')
outfile.write(' static const int Offset = %s; \n' % parser.op_right_offset)
outfile.write('}\n\n')
outfile.write('namespace OperatorRights\n')
outfile.write('{\n')
globalOffset = parser.op_right_offset << 24
for r in parser.op_rights:
outfile.write('enum %s\n' % r.name)
outfile.write('{\n')
offsetPlugin = globalOffset + (r.offset << 16)
groupCount = -1
for group in r.groups:
groupCount = groupCount + 1
groupOffset = offsetPlugin + (groupCount << 8)
for k,v,d in group.values:
v += groupOffset
outfile.write(' %s = %s,\n' % (k, hex(v)))
outfile.write('};\n')
outfile.write('\n')
outfile.write('enum Groups_%s\n' % parser.module_name)
outfile.write('{\n')
pattern = re.compile(r'\W+')
for r in parser.op_rights:
offsetPlugin = globalOffset + (r.offset << 16)
for group in r.groups:
groupOffset = offsetPlugin + (groupCount << 8)
name = pattern.sub('', group.name)
outfile.write(' Group%s = %s,\n' % (name, hex(groupOffset)))
outfile.write('};\n')
outfile.write('\n')
outfile.write('enum Plugins_%s\n' % parser.module_name)
outfile.write('{\n')
for r in parser.op_rights:
offsetPlugin = globalOffset + (r.offset << 16)
name = pattern.sub('', r.name)
outfile.write(' Plugin%s = %s,\n' % (name, hex(offsetPlugin)))
outfile.write('};\n')
outfile.write('} // namespace OperatorRights\n')
class OperatorRightsCpp(object):
def generate( self, outfile, parser ):
outputHeader( outfile, parser, 'Operator Rights (source)', False, False )
outfile.write('#include "%s_operator_rights.h"\n' % parser.service_abbr)
outfile.write('#include <QCoreApplication>\n\n\n')
if len(parser.op_rights) == 0:
return
outfile.write('\nvoid doNothing()\n{\n const char * txt[] {\n')
disamb = 'OperatorRights'
globalOffset = parser.op_right_offset << 24
for r in parser.op_rights:
offsetPlugin = globalOffset + (r.offset << 16)
groupCount = -1
for group in r.groups:
groupCount = groupCount + 1
groupOffset = offsetPlugin + (groupCount << 8)
if group.name != '':
outfile.write(' QT_TRANSLATE_NOOP("%s", "%s"),\n' % (disamb, group.name))
if group.desc != '':
outfile.write(' QT_TRANSLATE_NOOP("%s", "%s"),\n' % (disamb, group.desc))
for aclName, aclId, aclDescr in group.values:
outfile.write(' QT_TRANSLATE_NOOP("%s", "%s"),\n' % (disamb, aclDescr))
outfile.write(' };\n Q_UNUSED(txt);\n}\n')
class OperatorRightsSql(object):
def generate( self, outfile, parser ):
outfile.write('BEGIN;\n')
globalOffset = parser.op_right_offset << 24
if globalOffset > 0:
outfile.write('--- Offset: %s = %s\n' % (globalOffset, hex(globalOffset)))
groupId = globalOffset;
for r in parser.op_rights:
offsetPlugin = globalOffset + (r.offset << 16)
groupCount = -1
for group in r.groups:
groupCount = groupCount + 1
groupOffset = offsetPlugin + (groupCount << 8)
outfile.write('\n--- %s - %s = %s\n' % (group.name, hex(groupOffset), groupOffset))
str = "INSERT INTO opm.opm_group (id, name, description, type_id, is_hidden) VALUES (%s, '%s', '%s', 4, false);\n"
outfile.write(str % (groupOffset, group.name, group.desc))
for aclName, aclId, aclDescr in group.values:
aclId += groupOffset
outfile.write('--- Base: %s, %s - %s = %s\n' % (groupOffset, aclName, aclId, hex(aclId)))
str = "INSERT INTO opm.opm_access_control_level (id, name, description) VALUES (%s, '%s', '%s');\n";
outfile.write(str % (aclId, aclName, aclDescr));
str = "INSERT INTO opm.opm_group (id, name, description, type_id, is_hidden) VALUES (%s, '%s', '%s', 1, true);\n";
outfile.write(str % (groupId, aclName, aclDescr));
str = "INSERT INTO opm.opm_acl_allow (group_id, acl_id) VALUES (%s, %s); -- group_id is: %s = %s\n";
outfile.write(str % (groupId, aclId, groupId, hex(groupId)));
str = "INSERT INTO opm.opm_group_group (group_id, is_member_of_group_id) VALUES (%s, %s); -- %s\n"
outfile.write(str % (groupOffset, groupId, group.name))
groupId = groupId + 1
outfile.write('\n')
outfile.write('END;\n')
def generate( parser, generator, out_filename ):
try:
outfile = open(out_filename, 'w')
except:
print("Could not open %s for writing" % out_filename, file=sys.stderr)
sys.exit(4)
generator.generate(outfile, parser)
outfile.close()
def usage( error ):
print(error, file=sys.stderr)
print("Usage: %s DTS|DTC|FTS|FTC|SQL <service_file.xml> <outfile> " % sys.argv[0], file=sys.stderr)
print(" %s ALL <service_file.xml> <outdir> " % sys.argv[0], file=sys.stderr)
print("i.e.: %s DTS exm_service.xml DtsTypeDefinitions.h" % sys.argv[0], file=sys.stderr)
print("call: '%s'" % " ".join(sys.argv), file=sys.stderr)
sys.exit(1)
def main():
if len(sys.argv) != 4:
usage("Invalid argument count")
option_str = sys.argv[1]
xml_filename = sys.argv[2]
parser = FileParser()
parser.build(xml_filename)
if option_str == "DTS":
generate(parser, DtsGenerator(), sys.argv[3])
elif option_str == "DTC":
generate(parser, DtcGenerator(), sys.argv[3])
elif option_str == "FTS":
generate(parser, FtsGenerator(), sys.argv[3])
elif option_str == "FTC":
generate(parser, FtcGenerator(), sys.argv[3])
elif option_str == "SQL":
out_dir = sys.argv[3]
generate(parser, OperatorRightsSql(), os.path.join(out_dir, parser.service_abbr + '_operator_rights.sql'))
elif option_str == "ALL":
out_dir = sys.argv[3]
fn = out_dir + '/' + parser.service_abbr + '_%s_types.h'
generate(parser, DtsGenerator(), fn % 'dts')
generate(parser, DtcGenerator(), fn % 'dtc')
generate(parser, FtsGenerator(), fn % 'fts')
generate(parser, FtcGenerator(), fn % 'ftc')
generate(parser, OperatorRights(), out_dir + '/' + parser.service_abbr + '_operator_rights.h')
generate(parser, OperatorRightsCpp(), out_dir + '/' + parser.service_abbr + '_operator_rights.cpp')
else:
usage("invalid option '%s'" % option_str)
sys.exit(0)
if __name__ == "__main__":
main()
File diff suppressed because it is too large. Load diff
@@ -1,167 +0,0 @@
#include "CppTopicGeneratorGrantlee.h"
#include "DaoGeneratorGrantlee.h"
#include "DatabaseGeneratorGrantlee.h"
#include "SqlGenerator.h"
#include "StructBuilder.h"
#include "XmlReader.h"
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <NGLLogger/INGLLogger.h>
#include <QtCore/QDir>
#include <nglapplication/nglcoreapplication.h>
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser() : LtsCommandLineParser(LtsCommandLineParser::LtsCommandLineParser::None)
{
addOption({QStringList() << QStringLiteral("s") << QStringLiteral("servicefile"),
QStringLiteral("full path to the service xml-file"), QStringLiteral("service xml-file"),
QStringLiteral("/home")});
addOption({QStringList() << QStringLiteral("a") << QStringLiteral("action"),
QStringLiteral("the action to execute"), QStringLiteral("DATABASE|DATABASE_SERVER|IDL|DDS_TOPICS"),
QStringLiteral("/0")});
addOption({QStringList() << QStringLiteral("o") << QStringLiteral("outdir"),
QStringLiteral("the output directory where to put the generated files"),
QStringLiteral("output directory"), QStringLiteral("/home")});
addOption({QStringList() << QStringLiteral("i") << QStringLiteral("idlinfo"),
QStringLiteral("the info file with all available idl service files"),
QStringLiteral("info file for idl"), QStringLiteral("/0")});
}
bool init()
{
if (!LtsCommandLineParser::init())
return false;
if (serviceFile().isEmpty() || action().isEmpty() || outDir().isEmpty())
{
showHelp();
return false;
}
return true;
}
QString serviceFile() const
{
return value(QStringLiteral("s"));
}
QString action() const
{
return value(QStringLiteral("a"));
}
QString outDir() const
{
return value(QStringLiteral("o"));
}
QString idlInfoFile() const
{
return value(QStringLiteral("i"));
}
};
int main(int argc, char** argv)
{
NGLCoreApplication app(argc, argv);
app.createLogger("CodeGen", "codegen_log4cxx.props", true);
NGL::INGLLogger& m_logger = app.baseLogger();
CmdLineParser options;
if (!options.init())
return -1;
QScopedPointer<CodeGen::XmlReader> reader(new CodeGen::XmlReader);
DEBUG_LOG(m_logger) << "ServiceFile: " << options.serviceFile();
DEBUG_LOG(m_logger) << "IdlInfoFile: " << options.idlInfoFile();
if (!reader->readServiceFile(options.serviceFile(), options.idlInfoFile()))
{
ERROR_LOG(m_logger) << "Error parsing service file '" << options.serviceFile() << ": " << reader->errorString();
return 2;
}
QDir dir(options.outDir());
DEBUG_LOG(m_logger) << "Directory: " << options.outDir();
if (!dir.exists() && !dir.mkpath(options.outDir()))
{
ERROR_LOG(m_logger) << "Directory '" << options.outDir() << "' does not exist and can not be created!";
return 3;
}
QScopedPointer<CodeGen::StructBuilder> structBuilder(new CodeGen::StructBuilder(reader.data()));
if (!structBuilder->buildStructures())
{
ERROR_LOG(m_logger) << "Error building up structures";
return 4;
}
const QString action = options.action().toLower();
QScopedPointer<CodeGen::GeneratorGrantleeBase> generator;
if (action == QStringLiteral("dao"))
generator.reset(new CodeGen::DaoGeneratorGrantlee);
else if (action == QStringLiteral("database"))
generator.reset(new CodeGen::DatabaseGeneratorGrantlee(false));
else if (action == QStringLiteral("database_server"))
generator.reset(new CodeGen::DatabaseGeneratorGrantlee(true));
else if (action == QStringLiteral("dds_topics"))
generator.reset(new CodeGen::CppTopicGeneratorGrantlee);
else if (action == QStringLiteral("sql"))
{
CodeGen::SqlGenerator sql(reader.data(), structBuilder.data());
if (!sql.generateData(35))
return 8;
if (!sql.writeSql(dir.absolutePath() + QLatin1Char('/'), CodeGen::ExerciseDependency::Exercise))
{
ERROR_LOG(m_logger) << "Error while writing sql information for service " << sql.getServiceAbbr();
return 10;
}
if (!sql.generateData(42))
return 8;
if (!sql.writeSql(dir.absolutePath() + QLatin1Char('/'), CodeGen::ExerciseDependency::Exercise))
{
ERROR_LOG(m_logger) << "Error while writing sql information for service " << sql.getServiceAbbr();
return 10;
}
sql.generateData(0);
if (!sql.writeSql(dir.absolutePath() + QLatin1Char('/'), CodeGen::ExerciseDependency::Global))
{
ERROR_LOG(m_logger) << "Error while writing sql information for service " << sql.getServiceAbbr();
return 10;
}
return 0;
}
else
{
ERROR_LOG(m_logger) << "Invalid action '" << options.action() << "' given!";
return 5;
}
if (!generator->init(reader.data(), structBuilder.data()))
{
ERROR_LOG(m_logger) << "Error initializing generator!";
return 6;
}
const auto posArgs = options.positionalArguments();
for (const QString& file : posArgs)
{
if (!generator->generateData(file, dir.absolutePath()))
return 7;
}
return 0;
}
@@ -1,147 +0,0 @@
#include "main_grantlee_gui.h"
#include "CppTopicGeneratorGrantlee.h"
#include "DatabaseGeneratorGrantlee.h"
#include "StructBuilder.h"
#include "XmlReader.h"
#include "ui_main_grantlee_gui.h"
#include <NGLLogger/INGLLogger.h>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>
#include <nglapplication/nglapplication.h>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), m_xmlReader(nullptr), m_structBuilder(nullptr),
m_generator(new CodeGen::CppTopicGeneratorGrantlee), m_engine(new KTextTemplate::Engine), m_ui(new Ui_MainGrantleeGui)
{
m_idlServiceInfoFile = QStringLiteral("/home/chehrlic/TDS/TDS-build-product-clang/service_idl_files.txt");
m_ui->setupUi(this);
connect(m_ui->actionExit, &QAction::triggered, this, &MainWindow::close);
connect(m_ui->actionSelect_Data_Source, &QAction::triggered, this, &MainWindow::onSelectDataSource);
connect(m_ui->actionOpen_Template, &QAction::triggered, this, &MainWindow::onOpenTemplate);
connect(m_ui->actionSave_Template, &QAction::triggered, this, &MainWindow::onSaveTemplate);
connect(m_ui->pbRefresh, &QPushButton::clicked, this, &MainWindow::onRefreshOutput);
connect(m_ui->actionDatabaseGenerator, &QAction::triggered, this, &MainWindow::onGeneratorDb);
connect(m_ui->actionDDS_Generator, &QAction::triggered, this, &MainWindow::onGeneratorDds);
}
MainWindow::~MainWindow()
{
delete m_xmlReader;
delete m_structBuilder;
delete m_generator;
delete m_engine;
delete m_ui;
}
static QLatin1String s_startDir("/home/chehrlic/TDS/TDS-source-product/Lts/");
void MainWindow::onSelectDataSource()
{
const QString dataSource =
QFileDialog::getOpenFileName(this, tr("Select data source"), s_startDir, tr("Service Files (*.xml)"));
if (dataSource.isEmpty())
return;
delete m_xmlReader;
m_xmlReader = new CodeGen::XmlReader;
if (!m_xmlReader->readServiceFile(dataSource, m_idlServiceInfoFile))
{
QMessageBox::warning(this, tr("Invalid data source"),
tr("The selected data source is not valid: %1").arg(m_xmlReader->errorString()));
return;
}
delete m_structBuilder;
m_structBuilder = new CodeGen::StructBuilder(m_xmlReader);
if (!m_structBuilder->buildStructures())
{
QMessageBox::warning(this, tr("Invalid data source"), tr("Error building structures from data"));
return;
}
m_ui->teOuput->clear();
m_generator->init(m_xmlReader, m_structBuilder);
QMessageBox::information(this, tr("Data source"), tr("Successfully loaded data source"));
}
void MainWindow::onOpenTemplate()
{
const QString templateFile =
QFileDialog::getOpenFileName(this, tr("Select data source"), s_startDir, tr("Templates (*.grantlee)"));
if (templateFile.isEmpty())
return;
QFile f(templateFile);
if (!f.open(QIODevice::ReadOnly))
{
QMessageBox::warning(this, tr("Error reading template file"), tr("Can not read file: %1").arg(f.errorString()));
return;
}
const QString content = QString::fromUtf8(f.readAll());
m_ui->teInput->setText(content);
m_templateFile = templateFile;
}
void MainWindow::onSaveTemplate()
{
QFile f(m_templateFile);
if (!f.open(QIODevice::WriteOnly))
{
QMessageBox::warning(this, tr("Error saving template file"), tr("Can not save file: %1").arg(f.errorString()));
return;
}
f.write(m_ui->teInput->toPlainText().toUtf8());
f.close();
QMessageBox::information(this, tr("Saving template file"),
tr("Successfully saved template to %1").arg(m_templateFile));
}
void MainWindow::onRefreshOutput()
{
const QString in = m_ui->teInput->toPlainText();
if (!m_generator->setTemplate(QString(), in))
{
m_ui->teOuput->setText(m_generator->errorString());
return;
}
m_ui->teOuput->setText(m_generator->render());
}
void MainWindow::onGeneratorDb()
{
if (dynamic_cast<CodeGen::DatabaseGeneratorGrantlee*>(m_generator))
return;
delete m_generator;
m_generator = new CodeGen::DatabaseGeneratorGrantlee(false);
if (m_xmlReader && m_structBuilder)
m_generator->init(m_xmlReader, m_structBuilder);
}
void MainWindow::onGeneratorDds()
{
if (dynamic_cast<CodeGen::CppTopicGeneratorGrantlee*>(m_generator))
return;
delete m_generator;
m_generator = new CodeGen::CppTopicGeneratorGrantlee;
if (m_xmlReader && m_structBuilder)
m_generator->init(m_xmlReader, m_structBuilder);
}
int main(int argc, char** argv)
{
NGLApplication app(argc, argv);
app.createLogger("CodeGen", "codegen_log4cxx.props");
NGL::INGLLogger& m_logger = app.baseLogger();
STARTUP_LOG(m_logger);
int iRet = 0;
{
QScopedPointer<MainWindow> mw(new MainWindow);
mw->show();
iRet = app.exec();
}
SHUTDOWN_LOG(m_logger);
return iRet;
}
@@ -1,42 +0,0 @@
#pragma once
#include <QtWidgets/QMainWindow>
#include <QtCore/QObject>
namespace CodeGen
{
class XmlReader;
class StructBuilder;
class GeneratorGrantleeBase;
} // namespace CodeGen
namespace KTextTemplate
{
class Engine;
}
class Ui_MainGrantleeGui;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow();
private Q_SLOTS:
void onSelectDataSource();
void onOpenTemplate();
void onSaveTemplate();
void onRefreshOutput();
void onGeneratorDb();
void onGeneratorDds();
private:
CodeGen::XmlReader* m_xmlReader;
CodeGen::StructBuilder* m_structBuilder;
CodeGen::GeneratorGrantleeBase* m_generator;
KTextTemplate::Engine* m_engine;
QString m_idlServiceInfoFile;
QString m_templateFile;
Ui_MainGrantleeGui* m_ui;
};
@@ -1,140 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainGrantleeGui</class>
<widget class="QMainWindow" name="MainGrantleeGui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>838</width>
<height>532</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Input</string>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="teInput"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Output</string>
</property>
</widget>
</item>
<item row="0" column="1">
<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="2">
<widget class="QPushButton" name="pbRefresh">
<property name="text">
<string>Refresh Output</string>
</property>
<property name="shortcut">
<string>Ctrl+R</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QTextBrowser" name="teOuput"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>838</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>Fi&amp;le</string>
</property>
<addaction name="actionSelect_Data_Source"/>
<addaction name="actionOpen_Template"/>
<addaction name="actionSave_Template"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuGenerator">
<property name="title">
<string>&amp;Generator</string>
</property>
<addaction name="actionDatabaseGenerator"/>
<addaction name="actionDDS_Generator"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuGenerator"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionExit">
<property name="text">
<string>&amp;Exit</string>
</property>
</action>
<action name="actionOpen_Template">
<property name="text">
<string>&amp;Open Template</string>
</property>
</action>
<action name="actionSelect_Data_Source">
<property name="text">
<string>&amp;Select Data Source</string>
</property>
</action>
<action name="actionSave_Template">
<property name="text">
<string>Sa&amp;ve Template</string>
</property>
</action>
<action name="actionDatabaseGenerator">
<property name="text">
<string>&amp;DatabaseGenerator</string>
</property>
</action>
<action name="actionDDS_Generator">
<property name="text">
<string>DDS &amp;Generator</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,39 +0,0 @@
/**
* @RDE_HEADER@
* Template for idl struct definitions
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-05
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}.idl
#ifndef {{ namespace_abbr }}_idl_included
#define {{ namespace_abbr }}_idl_included
//@copy #include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> // #define NDDSUSERDllExport for linux
//@copy #include "{{ namespace_abbr }}_enums.hpp"
#include "{{ namespace_abbr }}_enums.idl"
{% for ext_service in ext_services.member %}
//@copy #include <{{ ext_service.baseFilePath }}.hpp>
#include <{{ ext_service.baseFilePath }}.idl>
{% endfor %}
module {{ namespace }}
{
{% for idl_struct in idl_structs %}
{% if idl_struct.comment %} // {{ idl_struct.comment }} {% endif %}
struct {{ idl_struct.name }} {% if idl_struct.baseValueType %}: {{ idl_struct.baseValueType }} {% endif %}
{ {% for idl_member in idl_struct.member %}
{{ idl_member.type }} {{ idl_member.name }}; {{ idl_member.isKey }} {{ idl_member.isOptional }} {% endfor %}
}; //@Extensibility {{ idl_struct.extensibility }}
{% endfor %}
}; // namespace {{ namespace }}
#endif
{% endautoescape %}
@@ -1,204 +0,0 @@
/**
* @RDE_HEADER@
* Template for 'real' structures including insert/update/delete functions for db
* $Id: svc_db_impl.cpp.grantlee 56901 2022-04-22 04:21:30Z karwat $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/svc_db_impl.cpp.grantlee $
* $Author: karwat $
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dao.cpp
#include "{{ namespace_abbr }}_dao.h"
{% if custom_db_procs %}
#include <{{ include_prefix }}/{{ namespace_abbr }}_db_impl_custom.h>
{% endif %}
#include <ngldb/dbtransaction.h>
#include <ngldb/nsqlquery.h>
#include <ngldb/dbconnection.h>
#include <ngldb/sql_query_janitor.h>
#include <QtSql/QSqlError>
#include <QVariant>
#include <NGLLogger/INGLLogger.h>
namespace {{ namespace }}
{
Dao::Dao(int exerciseId)
: m_query(nullptr)
, m_exerciseId(exerciseId)
, m_bEmitMessageOnError(true)
, m_bCanHandleSchemas(true)
, m_ownDatabaseConnection(true)
, m_logger(getLogger("Db"))
{}
Dao::~Dao()
{
m_query = nullptr;
{% if has_db_notifications %}
QSqlDriver *driver = m_dbConnection.driver();
if (driver && m_dbConnection.isOpen())
{
{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} driver->unsubscribeFromNotification(QStringLiteral("{{ db_notify }}"));
{% endfor %}{% endfor %} }
{% endif %}
if (m_ownDatabaseConnection && m_dbConnection.isOpen())
m_dbConnection.close();
}
bool Dao::init(const QString &dbUser,
const QString &dbPasswd,
const QString &dbDriver,
const QString &dbHost,
const QString &dbName)
{
m_ownDatabaseConnection = true;
m_dbConnection = DBConnection::open(m_logger, dbUser, dbPasswd, dbDriver, dbHost, dbName, schemaName());
return initInternal();
}
bool Dao::initInternal()
{
QSqlDriver *driver = m_dbConnection.driver();
if (!driver)
{
WARNING_LOG(m_logger) << "No SQL driver loaded - no database available!";
return false;
}
const QString driverName = m_dbConnection.driverName();
DEBUG_LOG(m_logger) << "Connected to database " << m_dbConnection.databaseName() << "@" << m_dbConnection.hostName()
<< ", schema: " << schemaName() << ", used driver: " << driverName;
if (!m_dbConnection.isOpen())
{
WARNING_LOG(m_logger) << "Database connection is no open - can not initialize database interface";
return false;
}
if (driverName == QLatin1String("QSQLITE"))
m_bCanHandleSchemas = false;
{% if has_db_notifications %}
if (!driver->hasFeature(QSqlDriver::EventNotifications))
{
WARNING_LOG(m_logger) << "Can not subscribe to notifications - sqldriver does not support this feature!";
}
else
{
connect(driver, QOverload<const QString&,QSqlDriver::NotificationSource,const QVariant&>::of(&QSqlDriver::notification),
this, &Dao::onNotification);
{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %}driver->subscribeToNotification(QStringLiteral("{{ db_notify }}"));{% endfor %}{% endfor %}
}
{% endif %}
m_query = std::unique_ptr<NSqlQuery>(new NSqlQuery(m_dbConnection, m_logger));
return true;
}
QString Dao::schemaName() const
{
if (!m_bCanHandleSchemas)
return QString();
if (m_exerciseId <= 0)
return QStringLiteral("{{ namespace_abbr }}");
return DBCommon::getExerciseSchemaName(m_exerciseId);
}
QString Dao::dbUser() const
{
if (m_exerciseId > 0)
return QStringLiteral("ets_ex_00");
else
return QStringLiteral("ets_") + schemaName();
}
void Dao::setEmitMessageOnError(bool bEmit)
{
m_bEmitMessageOnError = bEmit;
}
QString Dao::getTableName(const QString &tableName) const
{
if (!m_bCanHandleSchemas)
return tableName;
if (m_exerciseId > 0)
return DBCommon::getExerciseTableName(tableName, m_exerciseId);
return QStringLiteral("{{ namespace_abbr }}.") + tableName;
}
QSqlError Dao::getLastError() const
{
return m_lastError;
}
{% if has_db_notifications %}
void Dao::onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload)
{
DEBUG_LOG(m_logger) << "Notification source = " << source;
if (source == QSqlDriver::SelfSource)
return;
// search coresponding signal
{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %}
if (name == QStringLiteral("{{ db_notify }}"))
{
emit signal{{ db_notify }}(payload.toInt());
return;
}
{% endfor %}{% endfor %}
}
{% endif %}
{% for cpp_struct in cpp_structs %}
//
// --> Struct: Database::{{ cpp_struct.name }}
//
bool Dao::select(QVector<Database::{{ cpp_struct.name }}> &dataVec, const QString &where)
{
SqlQueryJanitor janitor(m_query);
const auto cb =[&dataVec] (const Database::{{ cpp_struct.name }} &data)
{
dataVec.push_back(data);
};
return selectPrepareInternal{{ cpp_struct.name }}(where) && selectFetchInternal{{ cpp_struct.name }}(cb);
}
bool Dao::selectPrepareInternal{{ cpp_struct.name }}(const QString &where)
{
const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.viewTableName }}")));
const QString stmt(QStringLiteral("SELECT {% for row in cpp_struct.viewMember %}{{ row.dbColumnNameView }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" FROM ") + tableName + where);
return m_query->prepare(stmt);
}
bool Dao::selectFetchInternal{{ cpp_struct.name }}(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb)
{
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
Database::{{ cpp_struct.name }} data;
while (m_query->next())
{
int i = 0;
{% for row in cpp_struct.viewMember %}
data.{{ row.cppMemberName }}{{ row.cppFromQvPre1 }} = {{ row.cppFromQvPre2 }}m_query->value(i++){{ row.cppFromQvPost }};{% endfor %}
cb(data);
}
return true;
}
// <-- Database::{{ cpp_struct.name }} end
{% endfor %}
/*static*/ QString Dao::qVariantToJsonString(const QVariant &value)
{
return value.isNull() ? QStringLiteral("{}") : value.toString();
}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,82 +0,0 @@
/**
* @RDE_HEADER@
* Template for 'real' structures including insert/update/delete functions for db
* $Id: svc_db_impl.h.grantlee 53676 2021-06-10 10:21:06Z diers $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/25_02_refactoring/Lts/Common/CodeGen/svc_db_impl.h.grantlee $
* $Author: diers $
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dao.h
#pragma once
#include <QPointF>
#include <QString>
#include <QVector>
#include <QSqlDriver>
#include <QSqlDatabase>
#include <QtSql/QSqlError>
#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp>
#include <{{ include_prefix }}/{{ namespace_abbr }}_db.h>
#include <{{ include_prefix }}/logger.h>
class NSqlQuery;
namespace {{ namespace }}
{
class {{ namespace_abbr_upper }}_COMMON_EXPORT Dao {% if not custom_db_procs %}final {%endif %}: public QObject
{
Q_OBJECT
public:
explicit Dao(int exerciseId);
~Dao();
bool init(const QString &dbUser,
const QString &dbPasswd,
const QString &dbDriver,
const QString &dbHost,
const QString &dbName);
QString schemaName() const;
QString dbUser() const;
const QSqlDatabase &database() const { return m_dbConnection; }
void setEmitMessageOnError(bool bEmit);
QSqlError getLastError() const;
{% for cpp_struct in cpp_structs %}
// --> Database::{{ cpp_struct.name }}
public:
bool select(QVector<Database::{{ cpp_struct.name }}> &dataVec, const QString &where = QString());
private:
bool selectPrepareInternal{{ cpp_struct.name }}(const QString &where);
bool selectFetchInternal{{ cpp_struct.name }}(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb);
// <-- Database::{{ cpp_struct.name }}
{% endfor %}
static QString qVariantToJsonString(const QVariant &value);
protected:
QString getTableName(const QString &tableName) const;
{% if has_db_notifications %}
private Q_SLOTS:
void onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload);
Q_SIGNALS:
{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %} void signal{{ db_notify }}(int id);
{% endfor %}{% endfor %}{% endif %}
protected:
QSqlDatabase m_dbConnection;
std::unique_ptr<NSqlQuery> m_query;
QSqlError m_lastError;
int m_exerciseId;
bool m_bEmitMessageOnError;
bool m_bCanHandleSchemas;
bool m_ownDatabaseConnection;
LOGGER_T m_logger;
private:
bool initInternal();
};// class Dao
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,145 +0,0 @@
/**
* @RDE_HEADER@
* Template for 'real' structures including insert/update/delete functions for db
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_db.h
#pragma once
#include <QObject>
#include <functional>
#include <QtSql/QSqlError>
#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_db_structs.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp>
{% for ext_service in ext_services.member %}
#include <{{ ext_service.baseFilePath }}.hpp>
{% endfor %}
namespace NGL
{
class INGLLogger;
}
class QSqlDatabase;
namespace {{ namespace }}
{
class {{ namespace_abbr_upper }}_COMMON_EXPORT DatabaseInterface : public QObject
{
Q_OBJECT
public:
virtual ~DatabaseInterface() = default;
/**
* Create a new instance for {{ namespace }}::DatabaseInterface.
* @param exerciseId if <= 0 -> global service
*/
static std::shared_ptr<DatabaseInterface> create(int exerciseId);
/**
* @brief initialize the database interface (must be called after the db connection was opened!)
* @return true on succcess
*/
virtual bool init(const QSqlDatabase &dbConnection) = 0;
virtual bool init(const QString &dbUser,
const QString &dbPasswd,
const QString &dbDriver,
const QString &dbHost,
const QString &dbName) = 0;
/**
* @brief return the current schema name depending if exercise id > 0 or not
*/
virtual QString schemaName() const = 0;
/**
* @brief return the current database user dependening if exercise id > 0 or not
*/
virtual QString dbUser() const = 0;
/**
* @brief return the database connection used in this DatabaseInterface
*/
virtual const QSqlDatabase &database() const = 0;
/**
* emit an error message (ERROR_LOG) when a statement fails to execute (default: on)
*/
virtual void setEmitMessageOnError(bool bEmit) = 0;
virtual QSqlError getLastError() const = 0;
{% for cpp_struct in cpp_structs %}
//
// Database::{{ cpp_struct.name }}
//
public:
virtual bool insert(Database::{{ cpp_struct.name }} &data) = 0;
virtual bool upsert(Database::{{ cpp_struct.name }} &data) = 0;
virtual bool update(const Database::{{ cpp_struct.name }} &data) = 0;
{% if cpp_struct.insertStructures|length == 1 %}
virtual bool insert(std::vector<Database::{{ cpp_struct.name }}> &cont) = 0;
virtual bool upsert(std::vector<Database::{{ cpp_struct.name }}> &cont) = 0;
virtual bool update(const std::vector<Database::{{ cpp_struct.name }}> &cont) = 0;
{% endif %}
virtual bool select(QVector<Database::{{ cpp_struct.name }}> &dataVec) = 0;
virtual bool select(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb) = 0;
{% for fk in cpp_struct.selectRemoveMember %}
virtual bool selectBy{{ fk.cppMemberNameCc }}(QVector<Database::{{ cpp_struct.name }}> &dataVec, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) = 0;
virtual bool selectBy{{ fk.cppMemberNameCc }}(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) = 0;
{% endfor %}
virtual bool select{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}, Database::{{ cpp_struct.name }} &data) = 0;
virtual bool remove{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}) = 0;
{% if cpp_struct.deleteWithoutPkAllowed %} {% for fk in cpp_struct.selectRemoveMember %}
virtual bool remove{{ cpp_struct.nameCc }}By{{ fk.cppMemberNameCc }}(const {{ fk.cppType }} &{{ fk.cppMemberName }}) = 0;
{% endfor %} {% endif %}
virtual bool fixUpSequence{{ cpp_struct.name }}() = 0;
virtual bool truncate{{ cpp_struct.name }}() = 0;
{% if cpp_struct.dbNotifications %}
Q_SIGNALS:
{% for dbnotify in cpp_struct.dbNotifications %}
// database notification on insert/update/delete
void {{ dbnotify }}(const QVariant &payload);
{% endfor %}
{% endif %}
//
// Database::{{ cpp_struct.name }} end
//
{% endfor %}
{% if data_structs|length > 0 %}
public:{% for data_struct in data_structs %}
// read only struct Database::{{ data_struct.name }}
virtual bool select(QVector<Database::{{ data_struct.name }}> &dataVec) = 0;
{% endfor %} {% endif %}
{% if custom_db_procs|length > 0 %}
public:{% for db_proc in custom_db_procs %}
// custom proc, body must be implemented by user
virtual {{ db_proc.signature }} = 0;
{% endfor %} {% endif %}
public:
// vector of database struct to/from JSON-array
// helper for BaseDownloader
template<typename T>
static QJsonArray toJson(const QVector<T> &dataVec)
{
return Database::toJson<T>(dataVec);
}
template<typename T>
static std::vector<T> fromJson(const QJsonArray &arr)
{
return Database::fromJson<T>(arr);
}
};// class DatabaseInterface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,732 +0,0 @@
/**
* @RDE_HEADER@
* Template for 'real' structures including insert/update/delete functions for db
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_db_impl.cpp
#include "{{ namespace_abbr }}_db_impl.h"
{% if custom_db_procs %}
#include <{{ include_prefix }}/{{ namespace_abbr }}_db_impl_custom.h>
{% endif %}
#include <ngldb/dbtransaction.h>
#include <ngldb/nsqlquery.h>
#include <ngldb/dbconnection.h>
#include <ngldb/sql_query_janitor.h>
#include <QtSql/QSqlError>
#include <QVariant>
#include <NGLLogger/INGLLogger.h>
namespace {{ namespace }}
{
QString qVariantToJsonString(const QVariant &value)
{
return value.isNull() ? QStringLiteral("{}") : value.toString();
}
QString fixupEmptyJsonString(const QString &str)
{
return str.isEmpty() ? QStringLiteral("{}") : str;
}
// DatabaseInterface
std::shared_ptr<DatabaseInterface> DatabaseInterface::create(int exerciseId)
{
{% if custom_db_procs %}
return std::make_shared<DatabaseInterfaceImplCustom>(exerciseId);
{% else %}
return std::make_shared<DatabaseInterfaceImpl>(exerciseId);
{% endif %}
}
DatabaseInterfaceImpl::DatabaseInterfaceImpl(int exerciseId)
: m_query(nullptr)
, m_exerciseId(exerciseId)
, m_bEmitMessageOnError(true)
, m_bCanHandleSchemas(true)
, m_ownDatabaseConnection(true)
, m_logger(getLogger("Db"))
{}
DatabaseInterfaceImpl::~DatabaseInterfaceImpl()
{
m_query = nullptr;
{% if has_db_notifications %}
QSqlDriver *driver = m_dbConnection.driver();
if (driver && m_dbConnection.isOpen())
{
{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %}
driver->unsubscribeFromNotification(QStringLiteral("{{ db_notify }}"));{% endfor %}{% endfor %}
}
{% endif %}
if (m_ownDatabaseConnection && m_dbConnection.isOpen())
m_dbConnection.close();
}
bool DatabaseInterfaceImpl::init(const QSqlDatabase &dbConnection)
{
m_dbConnection = dbConnection;
m_ownDatabaseConnection = false;
return initInternal();
}
bool DatabaseInterfaceImpl::init(const QString &dbUser,
const QString &dbPasswd,
const QString &dbDriver,
const QString &dbHost,
const QString &dbName)
{
m_ownDatabaseConnection = true;
m_dbConnection = DBConnection::open(m_logger, dbUser, dbPasswd, dbDriver, dbHost, dbName, schemaName());
return initInternal();
}
bool DatabaseInterfaceImpl::initInternal()
{
QSqlDriver *driver = m_dbConnection.driver();
if (!driver)
{
WARNING_LOG(m_logger) << "No SQL driver loaded - no database available!";
return false;
}
const QString driverName = m_dbConnection.driverName();
DEBUG_LOG(m_logger) << "Connected to database " << m_dbConnection.databaseName() << "@" << m_dbConnection.hostName()
<< ", schema: " << schemaName() << ", used driver: " << driverName;
if (!m_dbConnection.isOpen())
{
WARNING_LOG(m_logger) << "Database connection is no open - can not initialize database interface";
return false;
}
if (driverName == QLatin1String("QSQLITE"))
m_bCanHandleSchemas = false;
{% if has_db_notifications %}
if (!driver->hasFeature(QSqlDriver::EventNotifications))
{
WARNING_LOG(m_logger) << "Can not subscribe to notifications - sqldriver does not support this feature!";
}
else
{
connect(driver, QOverload<const QString&,QSqlDriver::NotificationSource,const QVariant&>::of(&QSqlDriver::notification),
this, &DatabaseInterfaceImpl::onNotification);
{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %}
driver->subscribeToNotification(QStringLiteral("{{ db_notify }}"));{% endfor %}{% endfor %}
}
{% endif %}
m_query = std::unique_ptr<NSqlQuery>(new NSqlQuery(m_dbConnection, m_logger));
return true;
}
QString DatabaseInterfaceImpl::schemaName() const
{
if (!m_bCanHandleSchemas)
return QString();
if (m_exerciseId <= 0)
return QStringLiteral("{{ namespace_abbr }}");
return DBCommon::getExerciseSchemaName(m_exerciseId);
}
QString DatabaseInterfaceImpl::dbUser() const
{
if (m_exerciseId > 0)
return QStringLiteral("ets_ex_00");
else
return QStringLiteral("ets_") + schemaName();
}
void DatabaseInterfaceImpl::setEmitMessageOnError(bool bEmit)
{
m_bEmitMessageOnError = bEmit;
}
QString DatabaseInterfaceImpl::getTableName(const QString &tableName) const
{
if (!m_bCanHandleSchemas)
return tableName;
if (m_exerciseId > 0)
return DBCommon::getExerciseTableName(tableName, m_exerciseId);
return QStringLiteral("{{ namespace_abbr }}.") + tableName;
}
QSqlError DatabaseInterfaceImpl::getLastError() const
{
return m_lastError;
}
{% if has_db_notifications %}
void DatabaseInterfaceImpl::onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload)
{
DEBUG_LOG(m_logger) << "Notification source = " << source;
// no need to emit notification signals
if (source == QSqlDriver::SelfSource)
return;
// search coresponding signal
{% for cpp_struct in cpp_structs %}{% for db_notify in cpp_struct.dbNotifications %}
if (name == QLatin1String("{{ db_notify }}"))
{
emit {{ db_notify }}(payload);
}{% endfor %}{% endfor %}
}
{% endif %}
{% for cpp_struct in cpp_structs %}
//
// Struct: Database::{{ cpp_struct.name }}
//
bool DatabaseInterfaceImpl::insert(Database::{{ cpp_struct.name }} &data)
{
SqlQueryJanitor janitor(m_query);
{% if cpp_struct.insertStructures|length > 1 %}
DBTransaction transaction(m_dbConnection);
{% endif %}
{% for insertStruct in cpp_struct.insertStructures %}
{
const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}")));
QString keyColumnInsertKey;
QString keyColumnInsertVal;
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
const bool bInsertWithKey = data.{{cpp_struct.primaryKey.cppMemberName}} != {{cpp_struct.primaryKey.cppDefault}};
if (bInsertWithKey)
{
keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, ");
keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, ");
}
{% endif %}
const QString stmt(QStringLiteral("INSERT INTO ") + tableName +
QStringLiteral(" (") + keyColumnInsertKey +
QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" ) VALUES (") + keyColumnInsertVal +
QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )"));
if (!m_query->prepare(stmt))
{
m_lastError = m_query->lastError();
return false;
}
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (bInsertWithKey)
m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }});
{% endif %}
{% for row in insertStruct.member %}
m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }});
{% endfor %}
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
{% if insertStruct.serialMember.cppMemberName %}
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}})
data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }};
{% else %}
data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }};
{% endif %}
{% if insertStruct.baseId.cppMemberName %}
data.{{ insertStruct.baseId.cppMemberName }} = data.{{ insertStruct.serialMember.cppMemberName }};
{% endif %}
{% endif %}
}
{% endfor %}
{% if cpp_struct.insertStructures|length > 1 %}
transaction.commit();
{% endif %}
return true;
}
bool DatabaseInterfaceImpl::upsert(Database::{{ cpp_struct.name }} &data)
{
SqlQueryJanitor janitor(m_query);
{% if cpp_struct.insertStructures|length > 1 %}
DBTransaction transaction(m_dbConnection);
{% endif %}
{% for insertStruct in cpp_struct.insertStructures %}
{
const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}")));
QString keyColumnInsertKey;
QString keyColumnInsertKeyConflict;
QString keyColumnInsertVal;
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
const bool bInsertWithKey = true;
keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, ");
keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, ");
{% endif %}
keyColumnInsertKeyConflict = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }} ");
const QString stmt(QStringLiteral("INSERT INTO ") + tableName +
QStringLiteral(" (") + keyColumnInsertKey +
QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" ) VALUES (") + keyColumnInsertVal +
QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )") +
QStringLiteral(" ON CONFLICT (") + keyColumnInsertKeyConflict + QStringLiteral(") DO UPDATE ") +
QStringLiteral(" SET"
" {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" WHERE excluded.{{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}"));
if (!m_query->prepare(stmt))
{
m_lastError = m_query->lastError();
return false;
}
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (bInsertWithKey)
m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }});
{% endif %}
{% for row in insertStruct.member %}
m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }});
{% endfor %}
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
{% if insertStruct.serialMember.cppMemberName %}
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}})
data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }};
{% else %}
data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }};
{% endif %}
{% if insertStruct.baseId.cppMemberName %}
data.{{ insertStruct.baseId.cppMemberName }} = data.{{ insertStruct.serialMember.cppMemberName }};
{% endif %}
{% endif %}
}
{% endfor %}
{% if cpp_struct.insertStructures|length > 1 %}
transaction.commit();
{% endif %}
return true;
}
bool DatabaseInterfaceImpl::update(const Database::{{ cpp_struct.name }} &data)
{
SqlQueryJanitor janitor(m_query);
{% if cpp_struct.insertStructures|length > 1 %}
DBTransaction transaction(m_dbConnection);
{% endif %}
{% for insertStruct in cpp_struct.insertStructures %}
{
const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}")));
const QString stmt(QStringLiteral("UPDATE ") + tableName +
QStringLiteral(" SET"
" {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" WHERE {{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}"));
if (!m_query->prepare(stmt))
{
m_lastError = m_query->lastError();
return false;
}
{% for row in insertStruct.member %}
m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }});
{% endfor %}
{% if insertStruct.serialMember.cppMemberName %}
m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }});
{% endif %}
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
}
{% endfor %}
{% if cpp_struct.insertStructures|length > 1 %}
transaction.commit();
{% endif %}
return true;
}
{% if cpp_struct.insertStructures|length == 1 %}
// current only implemented for 'plain' structures with only one affected table
bool DatabaseInterfaceImpl::insert(std::vector<Database::{{ cpp_struct.name }}> &cont)
{
if (cont.empty())
return true;
{% for insertStruct in cpp_struct.insertStructures %}
{
const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}")));
QString keyColumnInsertKey;
QString keyColumnInsertVal;
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
const bool bInsertWithKey = cont[0].{{cpp_struct.primaryKey.cppMemberName}} != {{cpp_struct.primaryKey.cppDefault}};
// the first element determines the stmt to be used
if (bInsertWithKey)
{
keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, ");
keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, ");
}
{% endif %}
const QString stmt(QStringLiteral("INSERT INTO ") + tableName +
QStringLiteral(" (") + keyColumnInsertKey +
QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" ) VALUES (") + keyColumnInsertVal +
QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )"));
{
SqlQueryJanitor janitor(m_query);
if (!m_query->prepare(stmt))
{
m_lastError = m_query->lastError();
return false;
}
for (auto &data : cont)
{
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (bInsertWithKey)
m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }});
{% endif %}
{% for row in insertStruct.member %}
m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }});
{% endfor %}
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
{% if insertStruct.serialMember.cppMemberName %}
if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}})
data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }};
{% endif %}
}
}
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (bInsertWithKey)
fixUpSequence{{ cpp_struct.nameCc }}();
{% endif %}
}
{% endfor %}
return true;
}
bool DatabaseInterfaceImpl::upsert(std::vector<Database::{{ cpp_struct.name }}> &cont)
{
if (cont.empty())
return true;
{% for insertStruct in cpp_struct.insertStructures %}
{
const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}")));
QString keyColumnInsertKey;
QString keyColumnInsertKeyConflict;
QString keyColumnInsertVal;
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
const bool bInsertWithKey = true;
keyColumnInsertVal = QStringLiteral(" :{{ insertStruct.primaryKey.dbColumnName }}, ");
keyColumnInsertKey = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }}, ");
{% endif %}
// the first element determines the stmt to be used
keyColumnInsertKeyConflict = QStringLiteral("{{ insertStruct.primaryKey.dbColumnName }} ");
const QString stmt(QStringLiteral("INSERT INTO ") + tableName +
QStringLiteral(" (") + keyColumnInsertKey +
QStringLiteral("{% for row in insertStruct.member %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" ) VALUES (") + keyColumnInsertVal +
QStringLiteral("{% for row in insertStruct.member %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} )") +
QStringLiteral(" ON CONFLICT (") + keyColumnInsertKeyConflict + QStringLiteral(") DO UPDATE ") +
QStringLiteral(" SET"
" {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" WHERE excluded.{{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}"));
{
SqlQueryJanitor janitor(m_query);
if (!m_query->prepare(stmt))
{
m_lastError = m_query->lastError();
return false;
}
for (auto &data : cont)
{
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (bInsertWithKey)
m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }});
{% endif %}
{% for row in insertStruct.member %}
m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }});
{% endfor %}
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
{% if insertStruct.serialMember.cppMemberName %}
if (data.{{cpp_struct.primaryKey.cppMemberName}} == {{cpp_struct.primaryKey.cppDefault}})
data.{{ insertStruct.serialMember.cppMemberName }} = m_query->lastInsertId(){{ insertStruct.serialMember.cppFromQvPost }};
{% endif %}
}
}
{% if forloop.first and cpp_struct.primaryKey.dbHasSequence %}
if (bInsertWithKey)
fixUpSequence{{ cpp_struct.nameCc }}();
{% endif %}
}
{% endfor %}
return true;
}
// current only implemented for 'plain' structures with only one affected table
bool DatabaseInterfaceImpl::update(const std::vector<Database::{{ cpp_struct.name }}> &cont)
{
if (cont.empty())
return true;
{% for insertStruct in cpp_struct.insertStructures %}
{
const QString tableName(getTableName(QStringLiteral("{{ insertStruct.name }}")));
const QString stmt(QStringLiteral("UPDATE ") + tableName +
QStringLiteral(" SET"
" {% for row in insertStruct.member %}{{ row.dbColumnName }} = :{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" WHERE {{ insertStruct.primaryKey.dbColumnName }} = :{{ insertStruct.primaryKey.dbColumnName }}"));
{
SqlQueryJanitor janitor(m_query);
if (!m_query->prepare(stmt))
{
m_lastError = m_query->lastError();
return false;
}
for (const auto &data : cont)
{
{% for row in insertStruct.member %}
m_query->bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.cppToQvPre }}data.{{ row.cppMemberName }}{{ row.cppToQvPost }});
{% endfor %}
{% if insertStruct.serialMember.cppMemberName %}
m_query->bindValue(QStringLiteral(":{{ insertStruct.primaryKey.dbColumnName }}"), data.{{ insertStruct.serialMember.cppMemberName }});
{% endif %}
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
}
}
}
{% endfor %}
return true;
}
{% endif %}
bool DatabaseInterfaceImpl::select(QVector<Database::{{ cpp_struct.name }}> &dataVec)
{
SqlQueryJanitor janitor(m_query);
const auto cb =[&dataVec] (const Database::{{ cpp_struct.name }} &data)
{
dataVec.push_back(data);
};
return selectPrepareInternal{{ cpp_struct.name }}(QString()) && selectFetchInternal{{ cpp_struct.name }}(cb);
}
bool DatabaseInterfaceImpl::select(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb)
{
SqlQueryJanitor janitor(m_query);
return selectPrepareInternal{{ cpp_struct.name }}(QString()) && selectFetchInternal{{ cpp_struct.name }}(cb);
}
{% for fk in cpp_struct.selectRemoveMember %}
bool DatabaseInterfaceImpl::selectBy{{ fk.cppMemberNameCc }}(QVector<Database::{{ cpp_struct.name }}> &dataVec, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy)
{
SqlQueryJanitor janitor(m_query);
if (!selectPrepareInternal{{ cpp_struct.name }}(QStringLiteral(" WHERE {{ fk.dbColumnName }} = :{{ fk.dbColumnName }}") + orderBy))
{
ERROR_LOG(m_logger) << "Error preparing query: " << m_query->lastError().text();
m_lastError = m_query->lastError();
return false;
}
m_query->bindValue(QStringLiteral(":{{ fk.dbColumnName }}"), {{ fk.cppToQvPre }}{{ fk.cppMemberName }}{{ fk.cppToQvPost }});
const auto cb =[&dataVec] (const Database::{{ cpp_struct.name }} &data)
{
dataVec.push_back(data);
};
return selectFetchInternal{{ cpp_struct.name }}(cb);
}
bool DatabaseInterfaceImpl::selectBy{{ fk.cppMemberNameCc }}(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy)
{
SqlQueryJanitor janitor(m_query);
if (!selectPrepareInternal{{ cpp_struct.name }}(QStringLiteral(" WHERE {{ fk.dbColumnName }} = :{{ fk.dbColumnName }}") + orderBy))
{
ERROR_LOG(m_logger) << "Error preparing query: " << m_query->lastError().text();
m_lastError = m_query->lastError();
return false;
}
m_query->bindValue(QStringLiteral(":{{ fk.dbColumnName }}"), {{ fk.cppToQvPre }}{{ fk.cppMemberName }}{{ fk.cppToQvPost }});
return selectFetchInternal{{ cpp_struct.name }}(cb);
}
{% endfor %}
bool DatabaseInterfaceImpl::selectPrepareInternal{{ cpp_struct.name }}(const QString &where)
{
const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.viewTableName }}")));
const QString stmt(QStringLiteral("SELECT {% for row in cpp_struct.viewMember %}{{ row.dbColumnNameView }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" FROM ") + tableName + where);
return m_query->prepare(stmt);
}
bool DatabaseInterfaceImpl::selectFetchInternal{{ cpp_struct.name }}(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb)
{
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
Database::{{ cpp_struct.name }} data;
while (m_query->next())
{
int i = 0;
{% for row in cpp_struct.viewMember %}
data.{{ row.cppMemberName }}{{ row.cppFromQvPre1 }} = {{ row.cppFromQvPre2 }}m_query->value(i++){{ row.cppFromQvPost }};{% endfor %}
cb(data);
}
return true;
}
bool DatabaseInterfaceImpl::select{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}, Database::{{ cpp_struct.name }} &data)
{
SqlQueryJanitor janitor(m_query);
int iCnt = 0;
const auto cb =[&data, &iCnt] (const Database::{{ cpp_struct.name }} &content)
{
data = content;
++iCnt;
};
const QString where = QStringLiteral(" WHERE {{ cpp_struct.primaryKey.dbColumnName }} = %1").arg({{ cpp_struct.primaryKey.cppMemberName }});
if (!selectPrepareInternal{{ cpp_struct.name }}(where) || !selectFetchInternal{{ cpp_struct.name }}(cb))
return false;
return (iCnt == 1);
}
bool DatabaseInterfaceImpl::remove{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }})
{
SqlQueryJanitor janitor(m_query);
const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}")));
const QString stmt(QStringLiteral("DELETE FROM ") + tableName +
QStringLiteral(" WHERE {{ cpp_struct.primaryKey.dbColumnName }} = ") +
QString::number({{ cpp_struct.primaryKey.cppMemberName }}));
if (!m_query->exec(stmt, m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
return true;
}
{% if cpp_struct.deleteWithoutPkAllowed %} {% for fk in cpp_struct.selectRemoveMember %}
bool DatabaseInterfaceImpl::remove{{ cpp_struct.nameCc }}By{{ fk.cppMemberNameCc }}(const {{ fk.cppType }} &{{ fk.cppMemberName }})
{
SqlQueryJanitor janitor(m_query);
const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}")));
const QString stmt(QStringLiteral("DELETE FROM ") + tableName +
QStringLiteral(" WHERE {{ fk.dbColumnName }} = :{{ fk.cppMemberName }}"));
if (!m_query->prepare(stmt))
{
ERROR_LOG(m_logger) << "Error preparing query: " << m_query->lastError().text();
m_lastError = m_query->lastError();
return false;
}
m_query->bindValue(QStringLiteral(":{{ fk.cppMemberName }}"), {{ fk.cppToQvPre }}{{ fk.cppMemberName }}{{ fk.cppToQvPost }});
return m_query->exec(m_bEmitMessageOnError);
}
{% endfor %} {% endif %}
bool DatabaseInterfaceImpl::fixUpSequence{{ cpp_struct.nameCc }}()
{
SqlQueryJanitor janitor(m_query);
const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}")));
const QString stmt(QStringLiteral("SELECT setval('") + tableName + QStringLiteral("_{{ cpp_struct.primaryKey.dbColumnName }}_seq', COALESCE((SELECT MAX({{ cpp_struct.primaryKey.dbColumnName }})+1 FROM ") +
tableName + QStringLiteral("), 1),false)"));
if (!m_query->exec(stmt, m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
return true;
}
bool DatabaseInterfaceImpl::truncate{{ cpp_struct.nameCc }}()
{
SqlQueryJanitor janitor(m_query);
const QString tableName(getTableName(QStringLiteral("{{ cpp_struct.primaryKey.dbTableName }}")));
const QString stmt(QStringLiteral("TRUNCATE TABLE ") + tableName + QStringLiteral(" CASCADE"));
if (!m_query->exec(stmt, m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
return true;
}
// Database::{{ cpp_struct.name }} end
{% endfor %}
{% for ds in data_structs %}
bool DatabaseInterfaceImpl::select(QVector<Database::{{ ds.name }}> &dataVec)
{
SqlQueryJanitor janitor(m_query);
const QString tableName(getTableName(QStringLiteral("{{ ds.viewTableName }}")));
const QString stmt(QStringLiteral("SELECT {% for row in ds.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}"
" FROM ") + tableName);
if (!m_query->exec(stmt, m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
Database::{{ ds.name }} data;
while (m_query->next())
{
int i = 0;
{% for row in ds.member %}
data.{{ row.cppMemberName }}{{ row.cppFromQvPre1 }} = {{ row.cppFromQvPre2 }}m_query->value(i++){{ row.cppFromQvPost }};{% endfor %}
dataVec.append(data);
}
return true;
}
{% endfor %}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,102 +0,0 @@
/**
* @RDE_HEADER@
* Template for 'real' structures including insert/update/delete functions for db
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_db_impl.h
#pragma once
#include <QPointF>
#include <QString>
#include <QVector>
#include <QSqlDriver>
#include <QSqlDatabase>
#include <QtSql/QSqlError>
#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp>
#include <{{ include_prefix }}/{{ namespace_abbr }}_db.h>
#include <{{ include_prefix }}/logger.h>
class NSqlQuery;
namespace {{ namespace }}
{
class {{ namespace_abbr_upper }}_COMMON_EXPORT DatabaseInterfaceImpl {% if not custom_db_procs %}final {%endif %}: public DatabaseInterface
{
Q_OBJECT
public:
explicit DatabaseInterfaceImpl(int exerciseId);
~DatabaseInterfaceImpl();
bool init(const QSqlDatabase &dbConnection) override final;
bool init(const QString &dbUser,
const QString &dbPasswd,
const QString &dbDriver,
const QString &dbHost,
const QString &dbName) override final;
QString schemaName() const override final;
QString dbUser() const override final;
const QSqlDatabase &database() const override final { return m_dbConnection; }
void setEmitMessageOnError(bool bEmit) override final;
QSqlError getLastError() const override final;
{% for cpp_struct in cpp_structs %}
// Database::{{ cpp_struct.name }}
bool insert(Database::{{ cpp_struct.name }} &data) override final;
bool upsert(Database::{{ cpp_struct.name }} &data) override final;
bool update(const Database::{{ cpp_struct.name }} &data) override final;
{% if cpp_struct.insertStructures|length == 1 %}
bool insert(std::vector<Database::{{ cpp_struct.name }}> &cont) override final;
bool upsert(std::vector<Database::{{ cpp_struct.name }}> &cont) override final;
bool update(const std::vector<Database::{{ cpp_struct.name }}> &cont) override final;
{% endif %}
bool select(QVector<Database::{{ cpp_struct.name }}> &dataVec) override final;
bool select(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb) override final;
{% for fk in cpp_struct.selectRemoveMember %}
bool selectBy{{ fk.cppMemberNameCc }}(QVector<Database::{{ cpp_struct.name }}> &dataVec, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) override final;
bool selectBy{{ fk.cppMemberNameCc }}(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb, const {{ fk.cppType }} &{{ fk.cppMemberName }}, const QString &orderBy = QString()) override final;
{% endfor %}
protected:
bool selectPrepareInternal{{ cpp_struct.name }}(const QString &where);
bool selectFetchInternal{{ cpp_struct.name }}(const std::function<void(const Database::{{ cpp_struct.name }} &data)> &cb);
public:
bool select{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}, Database::{{ cpp_struct.name }} &data) override final;
bool remove{{ cpp_struct.name }}({{ cpp_struct.primaryKey.cppType }} {{ cpp_struct.primaryKey.cppMemberName }}) override final;
{% if cpp_struct.deleteWithoutPkAllowed %} {% for fk in cpp_struct.selectRemoveMember %}
bool remove{{ cpp_struct.nameCc }}By{{ fk.cppMemberNameCc }}(const {{ fk.cppType }} &{{ fk.cppMemberName }}) override final;
{% endfor %} {% endif %}
bool fixUpSequence{{ cpp_struct.name }}() override final;
bool truncate{{ cpp_struct.name }}() override final;
// Database::{{ cpp_struct.name }}
{% endfor %}
{% for data_struct in data_structs %}
// read only struct{{ data_struct.name }}
bool select(QVector<Database::{{ data_struct.name }}> &dataVec) override final;
{% endfor %}
protected:
QString getTableName(const QString &tableName) const;
{% if has_db_notifications %}
private Q_SLOTS:
void onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload);
{% endif %}
protected:
QSqlDatabase m_dbConnection;
std::unique_ptr<NSqlQuery> m_query;
QSqlError m_lastError;
int m_exerciseId;
bool m_bEmitMessageOnError;
bool m_bCanHandleSchemas;
bool m_ownDatabaseConnection;
LOGGER_T m_logger;
private:
bool initInternal();
};// class DatabaseInterfaceImpl
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,112 +0,0 @@
/**
* @RDE_HEADER@
* Template for 'real' structures including insert/update/delete functions for db
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_db_structs.cpp
#include "{{ namespace_abbr }}_db_structs.h"
#include "{{ namespace_abbr }}_export.h"
namespace {{ namespace }}
{
namespace Database
{
QString jsonValueToJsonString(const QJsonValue &value)
{
return value.isNull() ? QStringLiteral("{}") : value.toString();
}
QString qVariantToJsonString(const QVariant &value)
{
return value.isNull() ? QStringLiteral("{}") : value.toString();
}
QString fixupEmptyJsonString(const QString &str)
{
return str.isEmpty() ? QStringLiteral("{}") : str;
}
// Basic type toJson overloads
QJsonValue toJson(int val)
{
return QJsonValue(val);
}
QJsonValue toJson(double val)
{
return QJsonValue(val);
}
QJsonValue toJson(qint64 val)
{
return QJsonValue(QString::number(val));
}
QJsonValue toJson(const std::string &val)
{
return QJsonValue(QString::fromStdString(val));
}
// QPointF
QJsonValue toJson(const QPointF &element)
{
QJsonObject obj;
obj.insert(QStringLiteral("x"), QJsonValue(element.x()));
obj.insert(QStringLiteral("y"), QJsonValue(element.y()));
return QJsonValue(obj);
}
template<>
QPointF fromJson(const QJsonValue &val)
{
QJsonObject obj = val.toObject();
return QPointF(obj.value(QStringLiteral("x")).toDouble(), obj.value(QStringLiteral("y")).toDouble());
}
{% if need_point3d %}
// QPoint3D
QJsonValue toJson(const LtsUtils::QPoint3D &element)
{
QJsonObject obj;
obj.insert(QStringLiteral("x"), QJsonValue(element.x()));
obj.insert(QStringLiteral("y"), QJsonValue(element.y()));
obj.insert(QStringLiteral("z"), QJsonValue(element.z()));
return QJsonValue(obj);
}
template<>
LtsUtils::QPoint3D fromJson(const QJsonValue &val)
{
QJsonObject obj = val.toObject();
return LtsUtils::QPoint3D(obj.value(QStringLiteral("x")).toDouble(), obj.value(QStringLiteral("y")).toDouble(), obj.value(QStringLiteral("z")).toDouble());
}
{% endif %}
{% for cpp_struct in cpp_structs %}
//
// Struct: {{ cpp_struct.name }}
//
QJsonObject toJson(const {{ cpp_struct.name }} &element)
{
QJsonObject obj;
{% for row in cpp_struct.jsonMember %}
{{ row.dbToJsonElement }};
{% endfor %}
return obj;
}
template<>
{{ cpp_struct.name }} fromJson(const QJsonValue &value)
{
const QJsonObject objElement(value.toObject());
{{ cpp_struct.name }} element;
{% for row in cpp_struct.jsonMember %}
{ {{ row.jsonElementToDb }}; } {% endfor %}
return element;
}
// {{ cpp_struct.name }} end
{% endfor %}
} // namespace Database
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,87 +0,0 @@
/**
* @RDE_HEADER@
* Template for 'real' structures including insert/update/delete functions for db
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_db_structs.h
#pragma once
#include <QDateTime>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QPointF>
#include <QString>
#include <QVariant>
#include <QVector>
{% if need_point3d %}
#include <LtsUtilsCore/qpoint3d.h>
{% endif %}
{% if need_pgarray %}
#include <LtsUtilsCore/pgarray.h>
{% endif %}
#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp>
{% for ext_service in ext_services.member %}
#include <{{ ext_service.baseFilePath }}.hpp>
{% endfor %}
namespace {{ namespace }}
{
namespace Database
{
{% for cpp_struct in cpp_structs %}
// {{ cpp_struct.name }}
struct {{ cpp_struct.name }}
{
{% for m in cpp_struct.cppMember %}{{ m.cppType }} {{ m.cppMemberName }}{% if m.cppDefault %} = {{ m.cppDefault }}{% endif %};
{% endfor %}
};{% endfor %}
{% for data_struct in data_structs %}
// read only struct {{ data_struct.name }}
struct {{ data_struct.name }}
{
{% for m in data_struct.cppMember %}{{ m.cppType }} {{ m.cppMemberName }}{% if m.cppDefault %} = {{ m.cppDefault }}{% endif %};
{% endfor %}
};{% endfor %}
// converting DbStructs to/from JSON
template<typename T>
T fromJson(const QJsonValue &value);
{% for cpp_struct in cpp_structs %}
// {{ cpp_struct.name }}
template <> {{ namespace_abbr_upper }}_{{ library_target }}_EXPORT {{ cpp_struct.name }} fromJson(const QJsonValue &value);
{{ namespace_abbr_upper }}_{{ library_target }}_EXPORT QJsonObject toJson(const {{ cpp_struct.name }} &element);{% endfor %}
// vector of database struct to/from JSON-array
template<typename T>
QJsonArray toJson(const QVector<T> &dataVec)
{
QJsonArray arr;
for (const T &data : dataVec)
arr.append(QJsonValue(toJson(data)));
return arr;
}
template<typename T>
std::vector<T> fromJson(const QJsonArray &arr)
{
std::vector<T> dataVec;
dataVec.reserve(arr.size());
for (const QJsonValue &val : arr)
dataVec.push_back(fromJson<T>(val));
return dataVec;
}
};// namespace Database
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,91 +0,0 @@
/**
* {{ namespace_abbr_upper }} Data Topic Client Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2016-01-04
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dtc.h
#pragma once
#include <QMap>
#include <QObject>
#include <functional>
#include <LtsUtilsCore/data_modification_enum.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp>
// Bring Core namespace types into scope for Interface namespace
using namespace {{ namespace }};
namespace DDSConnector
{
class DdsConnector;
}
namespace {{ namespace }}
{
namespace Interface
{
class {{ namespace_abbr_upper }}_COMMON_EXPORT Dtc : public QObject
{
Q_OBJECT
protected:
Dtc() = default;
public:
virtual ~Dtc() = default;
/**
* Create a new instance for {{ namespace }}::Interface::Dtc.
*/
static std::shared_ptr<Dtc> create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
/**
* A unique identifier for this DTC
*/
static inline const char *identifier() { return "{{ namespace }}.Interface.Dtc"; }
/**
* initialize this Dts instance with all data topic readers
*/
virtual bool enableAll(bool enable = true) = 0;
/**
* only initialize the specific reader (can be called instead enableAll())
*/
{% for dt in dataTopics.member %} {% if dt.dtcImpl %}
virtual bool enable{{ dt.name }}(bool enable = true) = 0;
virtual bool is{{ dt.name }}Enabled() const = 0;
virtual bool waitForHistoricalData{{ dt.name }}(int timeoutMSec = -1) = 0;
{% endif %} {% endfor %}
{% for cp in custom_dtc_procs %}
// custom procs, body must be implemented by user
virtual {{ cp.signature }} = 0;
{% endfor %}
{% for dt in dataTopics.member %}
public:
//
// Data Topic '{{ dt.name }}'
// {% if dt.key.name %}
virtual QMap<{{ dt.key.cppType }}, ::{{ namespace }}::{{ dt.type }}> {{ dt.nameFlc }}s() = 0;
virtual bool {{ dt.nameFlc }}s(const std::function<void(const ::{{ namespace }}::{{ dt.type }} &data)> &cb) = 0;
// when the cb returns true, the loop is stopped (useful when searching for a specific topic)
virtual bool {{ dt.nameFlc }}s(const std::function<bool(const ::{{ namespace }}::{{ dt.type }} &data)> &cb) = 0;
virtual ::{{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}) = 0;
{% else %}
virtual ::{{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}() = 0;
{% endif %}
Q_SIGNALS:
void {{ dt.nameFlc }}Changed(const ::{{ namespace }}::{{ dt.type }} &data, DataModification modType);
{% endfor %}
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,309 +0,0 @@
/**
* {{ namespace_abbr_upper }} Data Topic Client Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-07
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dtc_impl.cpp
#include "{{ namespace_abbr }}_dtc_impl.h"
{% if custom_dtc_procs %}
#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_impl_custom.h>
{% endif %}
#include <NGLLogger/INGLLogger.h>
#include <DDS/DTC/generic_dtc_impl.h>
#include <DDS/Core/logger.h>
#include <dds/core/cond/StatusCondition.hpp>
#include <dds/core/cond/WaitSet.hpp>
#include <unordered_map>
#include <QCoreApplication>
#include <QThread>
using namespace NGL;
using namespace Core;
namespace {{ namespace }}
{
namespace Interface
{
// initializer function
std::shared_ptr<Dtc> Dtc::create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
{
// share only for main thread
if (qApp->thread() != QThread::currentThread())
{% if custom_dtc_procs %}
return std::make_shared<DtcImplCustom>(ddsConnector);
{% else %}
return std::make_shared<DtcImpl>(ddsConnector);
{% endif %}
static std::unordered_map<DDSConnector::DdsConnector*, std::weak_ptr<Dtc>> s_availableConnectors;
auto it = s_availableConnectors.find(ddsConnector.get());
if (it != s_availableConnectors.end())
{
auto ret = it->second.lock();
if (ret)
return ret;
s_availableConnectors.erase(it);
}
{% if custom_dtc_procs %}
auto ret = std::make_shared<DtcImplCustom>(ddsConnector);
{% else %}
auto ret = std::make_shared<DtcImpl>(ddsConnector);
{% endif %}
s_availableConnectors.insert(std::make_pair(ddsConnector.get(), ret));
return ret; // moved implicitly (C++17; explicit std::move on a local triggers -Wredundant-move)
}
// DtcImpl
DtcImpl::DtcImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
: Dtc()
, m_ddsConnector(ddsConnector)
, m_logger(getLogger("Dtc"))
{}
DtcImpl::~DtcImpl()
{
enableAll(false);
}
bool DtcImpl::enableAll(bool enable)
{ {% for dt in dataTopics.member %} {% if dt.dtcImpl %} {% if dt.autosubscribe %}
if (!enable{{ dt.name }}(enable))
{
ERROR_LOG(m_logger) << "Error initializing data topic {{ dt.name }}";
return false;
}
{% endif %} {% endif %} {% endfor %}
return true;
}
{% for dt in dataTopics.member %} {% if dt.dtcImpl %} {% if dt.key.name %}
template <>
struct TopicConstraints<{{ dt.type }}>
{
using KEY = {{ dt.key.cppType }};
};
{% endif %} {% endif %} {% endfor %}
template <typename DTC>
bool DtcImpl::checkEnabled(const DTC &dtc) const
{
if (!dtc)
{
using TOPIC = typename DtcConstraints<DTC>::TOPIC ;
WARNING_LOG(m_logger) << "Error retrieving data topic "
<< DDS::topic_type_name<TOPIC>::value() << " - not enabled";
Q_ASSERT(false);
return false;
}
return true;
}
template <typename DTC>
bool DtcImpl::waitForHistoricalData(DTC &dtc, int timeoutMSec)
{
if (!checkEnabled(dtc))
return false;
return dtc->waitForHistoricalData(timeoutMSec);
}
template <typename T, typename K = typename TopicConstraints<T>::KEY>
K getKey(const T &val);
template <typename DTC,
typename T,
typename K>
QMap<K, T> DtcImpl::getAll(const DTC &dtc) const
{
if (!checkEnabled(dtc))
return {};
QMap<K, T> retMap;
for (const auto &sample : dtc->readAlive())
retMap.insert(getKey(sample.data()), sample);
return retMap;
}
template <typename DTC, typename C, typename T, typename K>
T DtcImpl::getOne(const DTC &dtc, const C &cont, const K &key) const
{
if (!checkEnabled(dtc))
return {};
const auto it = cont.find(key);
return it == cont.end() ? T() : it.value();
}
template <typename DTC, typename C, typename T>
bool DtcImpl::iterateAll(const DTC &dtc, const C &cont, const std::function<void(const T &data)> &cb) const
{
if (!checkEnabled(dtc))
return false;
for (const auto &sample : cont)
cb(sample);
return true;
}
template <typename DTC, typename C, typename T>
bool DtcImpl::iterateUntil(const DTC &dtc, const C &cont, const std::function<bool(const T &data)> &cb) const
{
if (!checkEnabled(dtc))
return false;
for (const auto &sample : cont)
{
if (cb(sample))
return true;
}
return false;
}
{% for dt in dataTopics.member %} {% if dt.dtcImpl %}
//
// Data Topic '{{ dt.name }}'
//
bool DtcImpl::enable{{ dt.name }}(bool enable)
{
if (enable)
{
if (m_dtc{{ dt.name }})
{
INFO_LOG(m_logger) << "{{ dt.name }} already enabled!";
return true;
}
m_dtc{{ dt.name }}.reset(m_ddsConnector->createDTC<DTC::{{ dt.name }}Impl>());
assert(m_dtc{{ dt.name }}.get());
m_dtc{{ dt.name }}->setSubscriberCallback([this](const {{ namespace }}::{{ dt.name }} &data, DataModification modType)
{
{{ dt.nameFlc }}ChangedInternal(data, modType);
});
}
else
{
m_dtc{{ dt.name }} = nullptr;
}
return true;
}
bool DtcImpl::waitForHistoricalData{{ dt.name }}(int timeoutMSec)
{
if (!is{{ dt.name }}Enabled())
{
enable{{ dt.name }}();
DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ;
}
if (!waitForHistoricalData(m_dtc{{ dt.name }}, timeoutMSec))
return false;
{% if dt.key.name %}
m_{{ dt.nameFlc }}Cont = getAll(m_dtc{{ dt.name }});
{% else %}
auto samples = m_dtc{{ dt.name }}->readAlive();
if (samples.length() == 0)
return false;
m_{{ dt.nameFlc }} = samples[samples.length() - 1].data();
{% endif %}
return true;
}
{% if dt.key.name %}
template <>
{{ dt.key.cppType }} getKey(const {{ dt.type }} &val)
{
return val.{{ dt.key.name }};
}
QMap<{{ dt.key.cppType }},::{{ namespace }}::{{ dt.type }}> DtcImpl::{{ dt.nameFlc }}s()
{
if (!is{{ dt.name }}Enabled())
{
enable{{ dt.name }}();
DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ;
waitForHistoricalData{{ dt.name }}(10);
}
return m_{{ dt.nameFlc }}Cont;
}
bool DtcImpl::{{ dt.nameFlc }}s(const std::function<void(const ::{{ namespace }}::{{ dt.type }} &data)> &cb)
{
if (!is{{ dt.name }}Enabled())
{
enable{{ dt.name }}();
DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ;
waitForHistoricalData{{ dt.name }}(10);
}
return iterateAll(m_dtc{{ dt.name }}, m_{{ dt.nameFlc }}Cont, cb);
}
bool DtcImpl::{{ dt.nameFlc }}s(const std::function<bool(const ::{{ namespace }}::{{ dt.type }} &data)> &cb)
{
if (!is{{ dt.name }}Enabled())
{
enable{{ dt.name }}();
DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ;
waitForHistoricalData{{ dt.name }}(10);
}
return iterateUntil(m_dtc{{ dt.name }}, m_{{ dt.nameFlc }}Cont, cb);
}
::{{ namespace }}::{{ dt.type }} DtcImpl::{{ dt.nameFlc }}({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }})
{
if (!is{{ dt.name }}Enabled())
{
enable{{ dt.name }}();
DEBUG_LOG(m_logger) << "NEED TO ENABLE ! {{ dt.name }}" ;
waitForHistoricalData{{ dt.name }}(10);
}
return getOne(m_dtc{{ dt.name }}, m_{{ dt.nameFlc }}Cont, {{ dt.key.nameFlc }});
}
void DtcImpl::{{ dt.nameFlc }}ChangedInternal(const ::{{ namespace }}::{{ dt.type }} &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
m_{{ dt.nameFlc }}Cont[getKey(data)] = data;
break;
case DataModification::eRemoved:
m_{{ dt.nameFlc }}Cont.remove(getKey(data));
break;
}
emit {{ dt.nameFlc }}Changed(data, modType);
}
{% else %}
::{{ namespace }}::{{ dt.type }} DtcImpl::{{ dt.nameFlc }}()
{
return m_{{ dt.nameFlc }};
}
void DtcImpl::{{ dt.nameFlc }}ChangedInternal(const ::{{ namespace }}::{{ dt.type }} &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
m_{{ dt.nameFlc }} = data;
break;
case DataModification::eRemoved:
m_{{ dt.nameFlc }} = ::{{ namespace }}::{{ dt.type }}();
break;
}
emit {{ dt.nameFlc }}Changed(data, modType);
}
{% endif %} {% endif %} {% endfor %}
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,109 +0,0 @@
/**
* {{ namespace_abbr_upper }} Data Topic Client Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-07
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dtc_impl.h
#pragma once
#include <DDS/DTC/generic_dtc.h>
#include <DDS/DTC/generic_dtc_impl.h>
#include <DDS/Generic/generic_data_reader.h>
#include <DDS/DDSConnector/dds_connector.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_types.h>
#include <{{ include_prefix }}/logger.h>
using namespace NGL;
#if !defined(MAKE_SVC{{ namespace_abbr_upper }}CLIENT_LIB)
#error "You are not allowed to include this file in your code!"
#endif
namespace {{ namespace }}
{
namespace Interface
{
template <typename UQ_DTC>
struct DtcConstraints
{
using DTC = typename UQ_DTC::element_type;
using TOPIC = typename DTC::TopicType;
};
template <typename TOPIC>
struct TopicConstraints
{};
class DtcImpl {% if not custom_dtc_procs %}final {%endif %}: public Dtc
{
Q_OBJECT
public:
explicit DtcImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
~DtcImpl();
bool enableAll(bool enable = true) override;
{% for dt in dataTopics.member %} {% if dt.dtcImpl %}
//
// Data Topic '{{ dt.name }}'
//
public:
bool enable{{ dt.name }}(bool enable = true) override final;
bool is{{ dt.name }}Enabled() const override final { return m_dtc{{ dt.name }} != nullptr; }
bool waitForHistoricalData{{ dt.name }}(int timeoutMSec = -1) override final;
{% if dt.key.name %}
QMap<{{ dt.key.cppType }}, ::{{ namespace }}::{{ dt.type }}> {{ dt.nameFlc }}s() override final;
bool {{ dt.nameFlc }}s(const std::function<void(const ::{{ namespace }}::{{ dt.type }} &data)> &cb) override final;
bool {{ dt.nameFlc }}s(const std::function<bool(const ::{{ namespace }}::{{ dt.type }} &data)> &cb) override final;
::{{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}) override final;
{% else %}
::{{ namespace }}::{{ dt.type }} {{ dt.nameFlc }}() override final;
{% endif %} {% endif %} {% endfor %}
private:
template <typename DTC>
bool checkEnabled(const DTC &dtc) const;
template <typename DTC>
bool waitForHistoricalData(DTC &dtc, int timeoutMSec);
template <typename DTC,
typename T = typename DtcConstraints<DTC>::TOPIC,
typename K = typename TopicConstraints<T>::KEY>
QMap<K, T> getAll(const DTC &dtc) const;
template <typename DTC, typename C,
typename T = typename DtcConstraints<DTC>::TOPIC,
typename K = typename TopicConstraints<T>::KEY>
T getOne(const DTC &dtc, const C &cont, const K &key) const;
template <typename DTC, typename C,
typename T = typename DtcConstraints<DTC>::TOPIC>
bool iterateAll(const DTC &dtc, const C &cont, const std::function<void(const T &data)> &cb) const;
template <typename DTC, typename C,
typename T = typename DtcConstraints<DTC>::TOPIC>
bool iterateUntil(const DTC &dtc, const C &cont, const std::function<bool(const T &data)> &cb) const;
protected:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
{% for dt in dataTopics.member %}
{% if dt.dtcImpl %}
void {{ dt.nameFlc }}ChangedInternal(const ::{{ namespace }}::{{ dt.type }} &data, DataModification modType);
std::unique_ptr<DTC::{{ dt.name }}> m_dtc{{ dt.name }}; {% if dt.key.name %}
QMap<{{ dt.key.cppType }}, ::{{ namespace }}::{{ dt.type }}> m_{{ dt.nameFlc }}Cont; {% else %}
::{{ namespace }}::{{ dt.type }} m_{{ dt.nameFlc }};
{% endif %} {% endif %} {% endfor %}
LOGGER_T m_logger;
signals:
{% for dt in dataTopics.member %}
{% if dt.dtcImpl %}
void {{ dt.nameFlc }}Changed(const ::{{ namespace }}::{{ dt.type }} &data, DataModification modType);
{% endif %} {% endfor %}
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,69 +0,0 @@
/**
* {{ namespace_abbr_upper }} Data Topic Server Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2016-01-05
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dts.h
#pragma once
#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp>
namespace DDSConnector
{
class DdsConnector;
}
namespace {{ namespace }}
{
namespace Interface
{
class Dts
{
protected:
Dts() = default;
public:
virtual ~Dts() = default;
/**
* Create a new instance for {{ namespace }}::Interface::Dts.
* The caller takes ownership of the returned instance
*/
{{ namespace_abbr_upper }}_SERVER_EXPORT static std::shared_ptr<Dts> create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
/**
* A unique identifier for this DTS
*/
{{ namespace_abbr_upper }}_SERVER_EXPORT static inline const char *identifier() { return "{{ namespace }}.Interface.Dts"; }
/**
* initialize this Dts instance with all data topic writers
*/
virtual bool enableAll(bool enable = true) = 0;
/**
* only initialize the specific writer (can be called instead enableAll())
*/
{% for dt in dataTopics.member %} {% if dt.dtsImpl %}
virtual bool enable{{ dt.name }}(bool enable = true) = 0;
virtual bool is{{ dt.name }}Enabled() const = 0;
{% endif %} {% endfor %}
{% for dt in dataTopics.member %}
//
// Data Topic '{{ dt.name }}'
//
virtual bool publish(const {{ namespace }}::{{ dt.type }} &data) = 0;
{% if dt.key.name %}
virtual bool remove{{ dt.name }}ByKey({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}, bool errorMsgIfSampleDoesNotExists = true) = 0;
{% endif %}
{% endfor %}
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,102 +0,0 @@
/**
* {{ namespace_abbr_upper }} Data Topic Server Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-07
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dts_impl.cpp
#include "{{ namespace_abbr }}_dts_impl.h"
#include <DDS/DTS/generic_dts_impl.h>
namespace {{ namespace }}
{
namespace Interface
{
// initializer function
std::shared_ptr<Dts> Dts::create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
{
return std::make_shared<DtsImpl>(ddsConnector);
}
// DtsImpl
DtsImpl::DtsImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
: Dts()
, m_ddsConnector(ddsConnector)
, m_logger(getLogger("Dts"))
{}
DtsImpl::~DtsImpl()
{
enableAll(false);
}
bool DtsImpl::enableAll(bool enable)
{
{% for dt in dataTopics.member %} {% if dt.dtsImpl %}
if (!enable{{ dt.name }}(enable))
{
WARNING_LOG(m_logger) << "Error initializing data topic {{ dt.name }}";
return false;
}
{% endif %} {% endfor %}
return true;
}
{% for dt in dataTopics.member %} {% if dt.dtsImpl %}
//
// Data Topic '{{ dt.name }}'
//
bool DtsImpl::enable{{ dt.name }}(bool enable)
{
if (enable)
{
if (m_dts{{ dt.name }})
{
INFO_LOG(m_logger) << "{{ ft.name }} already enabled!";
return true;
}
m_dts{{ dt.name }}.reset(m_ddsConnector->createDTS<DTS::{{ dt.name }}Impl>());
assert(m_dts{{ dt.name }}.get());
}
else
{
m_dts{{ dt.name }} = nullptr;
}
return true;
}
bool DtsImpl::publish(const {{ namespace }}::{{ dt.type }} &data)
{
if (!m_dts{{ dt.name }})
{
WARNING_LOG(m_logger) << "Error publishing data topic {{ dt.name }} - not enabled";
Q_ASSERT(false);
return false;
}
return m_dts{{ dt.name }}->write(data);
}
{% if dt.key.name %}
bool DtsImpl::remove{{ dt.name }}ByKey({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}, bool errorMsgIfSampleDoesNotExists)
{
if (!m_dts{{ dt.name }})
{
WARNING_LOG(m_logger) << "Error removing data topic {{ dt.name }} - not enabled";
Q_ASSERT(false);
return false;
}
{{ namespace }}::{{ dt.name }} data;
// rtiddsgen -stl generates plain public data members, so the key is
// assigned directly instead of the old setter call data.{{ dt.key.name }}(...).
data.{{ dt.key.name }} = {{ dt.key.name }};
return m_dts{{ dt.name }}->remove(data, errorMsgIfSampleDoesNotExists);
}
{% endif %}
{% endif %} {% endfor %}
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,58 +0,0 @@
/**
* {{ namespace_abbr_upper }} Data Topic Server Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-07
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_dts_impl.h
#pragma once
#include <QString>
#include <{{ include_prefix }}/{{ namespace_abbr }}_dts.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_dts_types.h>
#include <{{ include_prefix }}/logger.h>
#include <DDS/DDSConnector/dds_connector.h>
#if !defined(MAKE_SVC{{ namespace_abbr_upper }}SERVER_LIB)
#error "You are not allowed to include this file in your code!"
#endif
namespace {{ namespace }}
{
namespace Interface
{
class DtsImpl final : public Dts
{
public:
explicit DtsImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
~DtsImpl();
bool enableAll(bool enable = true) override;
{% for dt in dataTopics.member %} {% if dt.dtsImpl %}
//
// Data Topic '{{ dt.name }}'
//
bool enable{{ dt.name }}(bool enable = true) override;
bool is{{ dt.name }}Enabled() const override final { return m_dts{{ dt.name }} != nullptr; }
bool publish(const {{ namespace }}::{{ dt.type }} &data) override;
{% if dt.key.name %}
bool remove{{ dt.name }}ByKey({{ dt.key.cppTypeCr }} {{ dt.key.nameFlc }}, bool errorMsgIfSampleDoesNotExists = true) override;
{% endif %} {% endif %} {% endfor %}
protected:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
{% for dt in dataTopics.member %} {% if dt.dtsImpl %}
std::unique_ptr<DTS::{{ dt.name }}> m_dts{{ dt.name }};
{% endif %} {% endfor %}
LOGGER_T m_logger;
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,32 +0,0 @@
/**
* @RDE_HEADER@
* Template for idl struct definitions
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-19
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_enums.idl
#ifndef {{ namespace_abbr }}_enums_idl_included
#define {{ namespace_abbr }}_enums_idl_included
//@copy #include <{{ include_prefix }}/{{ namespace_abbr }}_export.h> // #define NDDSUSERDllExport for linux
module {{ namespace }}
{
{% for idl_enum in idl_enums %}
enum {{ idl_enum.name }}
{
{% for idl_member in idl_enum.member %}
{{ idl_member.key }} = {{ idl_member.value }}{% if not forloop.last %},{% endif %} {% endfor %}
};
{% endfor %}
}; // module {{ namespace }}
#endif
{% endautoescape %}
@@ -1,78 +0,0 @@
/**
* {{ namespace }} Function Topic Client interface
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_ftc.h
#pragma once
#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp>
#include <DDS/Core/function_reply_object.h>
namespace DDSConnector
{
class DdsConnector;
}
namespace {{ namespace }}
{
namespace Interface
{
class Ftc
{
protected:
Ftc() = default;
public:
virtual ~Ftc() = default;
/**
* Create a new instance for {{ namespace }}::Interface::Ftc.
* The caller takes ownership of the returned instance!
*/
{{ namespace_abbr_upper }}_CLIENT_EXPORT static std::shared_ptr<Ftc> create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector, bool forceCreate = false);
/**
* A unique identifier for this FTC
*/
{{ namespace_abbr_upper }}_CLIENT_EXPORT static inline const char *identifier() { return "{{ namespace }}.Interface.Ftc"; }
/**
* initialize this Ftc instance with all function topic clients
*/
virtual bool enableAll(bool enable = true) = 0;
/**
* only initialize the specific function topic client (can be called instead enableAll())
*/
{% for ft in functionTopics.member %}
virtual bool enable{{ ft.name }}(bool enable = true) = 0;
virtual bool is{{ ft.name }}Enabled() const = 0;
{% endfor %}
{% for ft in functionTopics.member %}
//
// Function Topic '{{ ft.name }}'
//
class {{ ft.replyHandler }}
{
public:
{{ ft.replyHandler }}() = default;
virtual ~{{ ft.replyHandler }}() = default;
virtual void {{ ft.onReply }}(const {{ ft.replyTypeFro }} &reply) = 0;
};
virtual int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, {{ ft.replyHandler }} *handler) = 0;
virtual int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, const std::function<void(const {{ ft.replyTypeFro }}&)> &cb) = 0;
virtual {% if ft.isCoreFt %}int32_t{% else %}{{ ft.replyTypeFro }}{% endif %} {{ ft.requestFlc }}(const {{ ft.requestType }} &request) noexcept(false) = 0; // await
virtual int64_t last{{ ft.request }}UniqueId() const = 0;
{% endfor %}
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,254 +0,0 @@
/**
* {{ namespace }} Function Topic Client implementation
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_ftc_impl.cpp
#include "{{ namespace_abbr }}_ftc_impl.h"
#include <DDS/FTC/generic_ftc_impl.h>
#include <LtsUtilsCore/await.h>
#include <QCoreApplication>
#include <QThread>
#include <unordered_map>
#include <chrono>
//using namespace std::literals::chrono_literals;
namespace {{ namespace }}
{
namespace Interface
{
// initializer function
Q_DECL_EXPORT std::shared_ptr<Ftc> Ftc::create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector, bool forceCreate)
{
// share only for main thread
if (qApp->thread() != QThread::currentThread() || forceCreate)
return std::make_shared<FtcImpl>(ddsConnector);
static std::unordered_map<DDSConnector::DdsConnector*, std::weak_ptr<Ftc>> s_availableConnectors;
auto it = s_availableConnectors.find(ddsConnector.get());
if (it != s_availableConnectors.end())
{
auto ret = it->second.lock();
if (ret)
return ret;
s_availableConnectors.erase(it);
}
auto ret = std::make_shared<FtcImpl>(ddsConnector);
s_availableConnectors.insert(std::make_pair(ddsConnector.get(), ret));
return ret; // moved implicitly (C++17; explicit std::move on a local triggers -Wredundant-move)
}
// FtcImpl
FtcImpl::FtcImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
: Ftc()
, m_ddsConnector(ddsConnector)
, m_logger(getLogger("Ftc"))
{}
bool FtcImpl::enableAll(bool enable)
{ {% for ft in functionTopics.member %}
if (!enable{{ ft.name }}(enable))
{
ERROR_LOG(m_logger) << "Error initializing function topic {{ ft.name }}";
return false;
}
{% endfor %}
return true;
}
FtcImpl::~FtcImpl()
{
enableAll(false);
}
{% for ft in functionTopics.member %}
// Function Topic '{{ ft.name }}'
bool FtcImpl::enable{{ ft.name }}(bool enable)
{
if (enable)
{
if (m_ftc{{ ft.name }})
{
INFO_LOG(m_logger) << "{{ ft.name }} already enabled!";
return true;
}
m_ftc{{ ft.name }}.reset(
m_ddsConnector->createFTC<FTC::{{ ft.name }}>(this));
assert(m_ftc{{ ft.name }}.get());
}
else
{
m_ftc{{ ft.name }} = nullptr;
}
return true;
}
{% endfor %}
template<typename T>
static T buildErrorReason(int reason, int requestId)
{
std::string errString;
std::vector<std::string> errArgs;
switch (reason)
{
case 1:
errString = tr_lts("dds", "Service could not handle request");
break;
case 3:
errString = tr_lts("dds", "Timeout while waiting for answer from server");
break;
default:
errString = tr_lts("dds", "Unknown error reason: %1");
errArgs.push_back(std::to_string(reason));
break;
}
T reply;
reply.id = requestId;
reply.errorString = errString;
reply.errorParams = errArgs;
return reply;
}
{% for ft in functionTopics.member %}
//
// {{ ft.name }}
//
int64_t FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request, {{ ft.replyHandler }} *handler)
{
return {{ ft.requestFlc }}(request, [ handler ](const {{ ft.replyTypeFro }} &reply) { if (handler) handler->{{ ft.onReply }}(reply); });
}
int64_t FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request, const std::function<void(const {{ ft.replyTypeFro }}&)> &cb)
{
if (!m_ftc{{ ft.name }})
{
WARNING_LOG(m_logger) << "Error requesting function topic {{ ft.name }} - not enabled";
Q_ASSERT(false);
return 0;
}
static const char * const slotReply = SLOT({{ ft.onReply }}({{ namespace }}::{{ ft.reply }},int,bool));
static const char * const slotError = SLOT({{ ft.onError }}(int,int,int));
int uniqueId = 0;
if (!m_ftc{{ ft.name }}->requestDataClassic(request, slotReply, slotError, uniqueId))
return 0;
m_{{ ft.replyHandlerFlc }}[uniqueId] = cb;
// first 16 hex bytes from Sha3_512 hash for '{{ ft.name }}' converted to an int64
m_last{{ ft.request }}UniqueId = uniqueId + {{ ft.replyHandlerHash }};
return m_last{{ ft.request }}UniqueId;
}
// NOTE: do not use if you call an FTC which returns an id while also containing an error as "error doesn't mean that the service might not reply with an id - so always take the id"
{% if ft.isCoreFt %}int32_t{% else %}{{ ft.replyTypeFro }}{% endif %} FtcImpl::{{ ft.requestFlc }}(const {{ ft.requestType }} &request) noexcept(false)
{
if (!m_ftc{{ ft.name }})
{
Q_ASSERT(false);
throw std::runtime_error("Error requesting function topic {{ ft.name }} - not enabled");
}
auto *requestWatcher = new Request2();
auto future = m_ftc{{ ft.name }}->requestDataModern(request, requestWatcher);
m_currentlyOpen_{{ ft.requestFlc }}Requests++;
//fprintf(stderr, "{{ ft.requestFlc }}: waiting for reply... (currently open requests: %d)\n", m_currentlyOpen_{{ ft.requestFlc }}Requests);
LtsUtils::await(requestWatcher, &Request2::finished, true); // blocks until request has reply or failed
m_currentlyOpen_{{ ft.requestFlc }}Requests--;
//fprintf(stderr, "{{ ft.requestFlc }}: request done!\n");
// the generic ftc impl's 'requestDataModern' + 'exec()' guarantees that calling 'future.get()' will not result in a 'wait()' call by 'get()' - but we should still check!
switch (future.wait_for(std::chrono::seconds(1)))
{
case std::future_status::deferred:
{
Q_ASSERT(false);
// fprintf(stderr, "{{ ft.requestFlc }}: deferred...\n");
break;
}
case std::future_status::timeout:
{
Q_ASSERT(false);
// fprintf(stderr, "{{ ft.requestFlc }}: future timeout!\n");
throw std::runtime_error("Future timeout!");
}
case std::future_status::ready:
{
// fprintf(stderr, "{{ ft.requestFlc }}: ready\n");
break;
}
}
{% if ft.isCoreFt %}
const auto _reply = future.get(); // throws if exception set
Core::FunctionReplyObject fro(
{% for rp in ft.replyParams %} _reply.{{ rp.nameGetter }}{% if not forloop.last %},{% endif %} {% endfor %}
);
fro.setUniqueId(m_last{{ ft.request }}UniqueId);
if (fro.error())
{
ERROR_LOG(m_logger) << "FTC call failed: " << fro.translated();
throw std::runtime_error(fro.translated().toStdString().c_str());
}
const auto reply = fro.id();
{% else %}
const auto reply = future.get(); // throws if exception set
{% endif %}
return reply;
}
void FtcImpl::{{ ft.onReply }}(const {{ namespace }}::{{ ft.reply }} &reply, int uniqueId, bool bRequestFinished)
{
auto it = m_{{ ft.replyHandlerFlc }}.find(uniqueId);
if (it == m_{{ ft.replyHandlerFlc }}.end())
{
WARNING_LOG(m_logger) << "Error replying function topic {{ ft.name }} - no replyHandler";
Q_ASSERT(false);
return;
}
auto cb = it.value();
if (bRequestFinished)
m_{{ ft.replyHandlerFlc }}.erase(it);
if (!cb)
return;
m_last{{ ft.request }}UniqueId = uniqueId + {{ ft.replyHandlerHash }};
{% if ft.isCoreFt %}
Core::FunctionReplyObject fro(
{% for rp in ft.replyParams %}
reply.{{ rp.nameGetter }}{% if not forloop.last %},{% endif %} {% endfor %}
);
fro.setUniqueId(m_last{{ ft.request }}UniqueId);
cb(fro);
{% else %}
cb(reply);
{% endif %}
}
void FtcImpl::{{ ft.onError }}(int reason, int uniqueId, int requestId)
{
{{ ft.onReply }}(buildErrorReason<{{ namespace }}::{{ ft.reply }}>(reason, requestId), uniqueId, true);
}
{% endfor %}
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,81 +0,0 @@
/**
* {{ namespace }} Function Topic Client implementation
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_ftc_impl.h
#pragma once
#include <{{ include_prefix }}/{{ namespace_abbr }}_ftc.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_ftc_types.h>
#include <{{ include_prefix }}/logger.h>
#include <DDS/FTC/abstract_requester.h>
#include <DDS/DDSConnector/dds_connector.h>
#include <QObject>
#include <QHash>
#include <QDebug>
#if !defined(MAKE_SVC{{ namespace_abbr_upper }}CLIENT_LIB)
#error "You are not allowed to include this file in your code!"
#endif
namespace {{ namespace }}
{
namespace Interface
{
class FtcImpl final : public QObject, public Ftc
{
Q_OBJECT
public:
explicit FtcImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
~FtcImpl();
bool enableAll(bool enable = true) override;
{% for ft in functionTopics.member %}
bool enable{{ ft.name }}(bool enable = true) override;
bool is{{ ft.name }}Enabled() const override { return m_ftc{{ ft.name }} != nullptr; }
{% endfor %}
{% for ft in functionTopics.member %}
//
// Function Topic '{{ ft.name }}'
//
/// Classic request:
public:
int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, {{ ft.replyHandler }} *handler) override; // returns the uniqueId used for this request
int64_t {{ ft.requestFlc }}(const {{ ft.requestType }} &request, const std::function<void(const {{ ft.replyTypeFro }}&)> &cb) override; // returns the uniqueId used for this request
int64_t last{{ ft.request }}UniqueId() const override { return m_last{{ ft.request }}UniqueId; }
private Q_SLOTS:
void {{ ft.onReply }}(const {{ namespace }}::{{ ft.reply }} &reply, int uniqueId, bool bRequestFinished);
void {{ ft.onError }}(int reason, int uniqueId, int requestId);
private:
QHash<int, std::function<void(const {{ ft.replyTypeFro }}&)>> m_{{ ft.replyHandlerFlc }};
int64_t m_last{{ ft.request }}UniqueId = -1;
/// Modern request:
public:
/* Executes the request and blocks (enters Qt's event loop) until the reply has arrived.
* NOTE: If reply has a FunctionReplyWithState, this function only returns the first reply and returns.
{% if ft.isCoreFt %} * Returns the id of the FunctionReply.{% endif %}*/
{% if ft.isCoreFt %}int32_t{% else %}{{ ft.replyTypeFro }}{% endif %} {{ ft.requestFlc }}(const {{ ft.requestType }} &request) noexcept(false) override;
private:
int m_currentlyOpen_{{ ft.requestFlc }}Requests { 0 };
std::unique_ptr<FTC::{{ ft.name }}> m_ftc{{ ft.name }};
{% endfor %}
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
LOGGER_T m_logger;
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,90 +0,0 @@
/**
* {{ namespace }} Function Topic Server interface
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_fts.h
#pragma once
#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp>
#include <DDS/Core/function_reply_object.h>
namespace DDSConnector
{
class DdsConnector;
}
namespace {{ namespace }}
{
namespace Interface
{
class Fts
{
protected:
Fts() = default;
public:
virtual ~Fts() = default;
/**
* Create a new instance for {{ namespace }}::Interface::Dts.
* The caller takes ownership of the returned instance
*/
{{ namespace_abbr_upper }}_SERVER_EXPORT static std::shared_ptr<Fts> create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
/**
* A unique identifier for this FTC
*/
{{ namespace_abbr_upper }}_SERVER_EXPORT static inline const char *identifier() { return "{{ namespace }}.Interface.Fts"; }
/**
* initialize this Fts instance with all function topic servers
*/
virtual bool enableAll(bool enable = true) = 0;
/**
* only initialize the specific function topic writer (can be called instead enableAll())
*/
{% for ft in functionTopics.member %}
virtual bool enable{{ ft.name }}(bool enable = true) = 0;
virtual bool is{{ ft.name }}Enabled() const = 0;
{% endfor %}
{% for ft in functionTopics.member %}
//
// Function Topic '{{ ft.name }}'
//
class {{ ft.requestHandler }}
{
public:
explicit {{ ft.requestHandler }}(const std::shared_ptr<Fts> &fts)
: m_fts(fts)
{
if (fts)
fts->set{{ ft.requestHandler }}(this);
}
virtual ~{{ ft.requestHandler }}()
{
auto sp = m_fts.lock();
if (sp)
sp->set{{ ft.requestHandler }}(nullptr);
}
virtual void {{ ft.onRequest }}(const rti::core::SampleIdentity &cookie,
const {{ ft.requestType }} &request) = 0;
private:
std::weak_ptr<Fts> m_fts;
};
virtual void set{{ ft.requestHandler }}({{ ft.requestHandler }} *handler) = 0;
virtual bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const {{ ft.replyType }} &reply) = 0;
{% if ft.isCoreFt %}
virtual bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const Core::FunctionReplyObject &replyObject) = 0;
{% endif %}
{% endfor %}
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,114 +0,0 @@
/**
* {{ namespace }} Function Topic Server implementation
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_fts_impl.cpp
#include "{{ namespace_abbr }}_fts_impl.h"
#include <DDS/FTS/generic_fts_impl.h>
namespace {{ namespace }}
{
namespace Interface
{
// initializer function
std::shared_ptr<Fts> Fts::create(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
{
return std::make_shared<FtsImpl>(ddsConnector);
}
// FtsImpl
FtsImpl::FtsImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
: Fts()
, m_ddsConnector(ddsConnector)
, m_logger(getLogger("Fts"))
{}
FtsImpl::~FtsImpl()
{
enableAll(false);
}
bool FtsImpl::enableAll(bool enable)
{
DEBUG_LOG(m_logger) << "FtsImpl::enableAll(" << (enable ? "true" : "false") << ")";
{% for ft in functionTopics.member %}
if (!enable{{ ft.name }}(enable))
{
ERROR_LOG(m_logger) << "Error initializing function topic {{ ft.name }}";
return false;
}
{% endfor %}
return true;
}
{% for ft in functionTopics.member %}
//
// Function Topic '{{ ft.name }}'
//
bool FtsImpl::enable{{ ft.name }}(bool enable)
{
if (enable)
{
if (m_fts{{ ft.name }})
{
INFO_LOG(m_logger) << "{{ ft.name }} already enabled!";
return true;
}
m_fts{{ ft.name }}.reset(m_ddsConnector->createFTS<FTS::{{ ft.name }}>());
assert(m_fts{{ ft.name }}.get());
m_fts{{ ft.name }}->setSubscriberCallback([this] (const rti::core::SampleIdentity &identity, const {{ namespace }}::{{ ft.request }} &request) { this->{{ ft.onRequest }}(identity, request); });
}
else
{
m_fts{{ ft.name }}.reset();
}
return true;
}
bool FtsImpl::{{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const {{ ft.replyType }} &reply)
{
if (!m_fts{{ ft.name }})
{
WARNING_LOG(m_logger) << "Error publishing function topic {{ dt.name }} - not enabled";
Q_ASSERT(false);
return false;
}
return m_fts{{ ft.name }}->sendReply(cookie, reply);
}
{% if ft.isCoreFt %}
bool FtsImpl::{{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const Core::FunctionReplyObject &replyObject)
{
return {{ ft.replyFlc }}(cookie, {{ ft.replyType }}(replyObject.id(), replyObject.str(), replyObject.args(), replyObject.additionalInfo()));
}
{% endif %}
void FtsImpl::set{{ ft.requestHandler }}({{ ft.requestHandler }} *handler)
{
m_{{ ft.requestHandlerFlc }} = handler;
}
void FtsImpl::{{ ft.onRequest }}(const rti::core::SampleIdentity &cookie,
const {{ namespace }}::{{ ft.request }} &request)
{
DEBUG_LOG(m_logger) << "{{ ft.onRequest }}";
if (!m_{{ ft.requestHandlerFlc }})
{
WARNING_LOG(m_logger) << "Incoming {{ ft.request }} without registered handler!";
Q_ASSERT(false);
return;
}
m_{{ ft.requestHandlerFlc }}->{{ ft.onRequest }}(cookie, request);
}
{% endfor %}
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,67 +0,0 @@
/**
* {{ namespace }} Function Topic Server implementation
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_fts_impl.h
#pragma once
#include <DDS/FTS/generic_fts_impl.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_fts.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_fts_types.h>
#include <{{ include_prefix }}/logger.h>
#include <DDS/DDSConnector/dds_connector.h>
#include <QObject>
#if !defined(MAKE_SVC{{ namespace_abbr_upper }}SERVER_LIB)
#error "You are not allowed to include this file in your code!"
#endif
namespace {{ namespace }}
{
namespace Interface
{
class FtsImpl final : public QObject, public Fts
{
Q_OBJECT
public:
explicit FtsImpl(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
~FtsImpl();
bool enableAll(bool enable = true) override;
{% for ft in functionTopics.member %}
//
// {{ ft.name }}
//
public:
bool enable{{ ft.name }}(bool enable = true) override;
bool is{{ ft.name }}Enabled() const override { return m_fts{{ ft.name }} != nullptr && m_{{ ft.requestHandlerFlc }} != nullptr; }
bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const {{ ft.replyType }} &reply) override;
{% if ft.isCoreFt %}
bool {{ ft.replyFlc }}(const rti::core::SampleIdentity &cookie, const Core::FunctionReplyObject &replyObject) override;
{% endif %}
void set{{ ft.requestHandler }}({{ ft.requestHandler }} *handler) override;
private Q_SLOTS:
void {{ ft.onRequest }}(const rti::core::SampleIdentity &cookie,
const {{ namespace }}::{{ ft.request }} &request);
private:
std::unique_ptr<FTS::{{ ft.name }}> m_fts{{ ft.name }};
{{ ft.requestHandler }} *m_{{ ft.requestHandlerFlc }} = nullptr;
{% endfor %}
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
LOGGER_T m_logger;
};
} // namespace Interface
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,172 +0,0 @@
/**
* Json <-> DDS conversion routines
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-11-30
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_json.cpp
#include "{{ namespace_abbr }}_json.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <LtsUtilsCore/dds_vector.h>
#include <dds/core/String.hpp>
namespace {{ namespace }}
{
template<>
int fromJson(const QJsonValue &val, type<int>)
{
return val.toInt();
}
template<>
double fromJson(const QJsonValue &val, type<double>)
{
return val.toDouble();
}
template<>
qint64 fromJson(const QJsonValue &val, type<qint64>)
{
return val.toString().toLongLong();
}
QJsonValue toJson(int vec)
{
return QJsonValue(vec);
}
QJsonValue toJson(double vec)
{
return QJsonValue(vec);
}
QJsonValue toJson(qint64 vec)
{
return QJsonValue(QString::number(vec));
}
// IDL 64-bit integers map to int64_t, which is 'long' on LP64 and therefore
// distinct from qint64 ('long long'); without this overload the generic
// toJson(std::vector<T>) is ambiguous for sequence<int64_t> members.
QJsonValue toJson(long vec)
{
return QJsonValue(QString::number(vec));
}
template<>
long fromJson(const QJsonValue &val, type<long>)
{
return val.toString().toLong();
}
// std::string
QJsonValue toJson(const std::string &element)
{
return QJsonValue(QString::fromStdString(element));
}
template<>
std::string fromJson(const QJsonValue &val, type<std::string>)
{
return val.toString().toStdString();
}
// std::vector<T>
template<typename T>
QJsonArray toJson(const std::vector<T> &vec)
{
QJsonArray arr;
for (const auto &element : vec)
arr.push_back(toJson(element));
return arr;
}
template<typename T>
std::vector<T> fromJson(const QJsonValue &val, type<std::vector<T>>)
{
std::vector<T> ret;
if (!val.isArray())
return ret;
const QJsonArray array(val.toArray());
ret.reserve(array.size());
for(int i = 0; i < array.size(); ++i)
ret.push_back(fromJson(array.at(i), type<T>{}));
return ret;
}
// dds::core:vector<uint8_t> (bytestream)
inline QJsonValue toJson(const std::vector<uint8_t> &element)
{
return QJsonValue(QString::fromLatin1(QByteArray::fromRawData(reinterpret_cast<const char *>(&element.at(0)),
static_cast<int>(element.size())).toBase64()));
}
template<>
std::vector<uint8_t> fromJson(const QJsonValue &val, type<std::vector<uint8_t>>)
{
const QByteArray ba(QByteArray::fromBase64(val.toString().toLatin1()));
return LtsUtils::convert<std::vector<uint8_t>>(ba);
}
{% for dt in jsonTopics %}
// {{ dt.name }}
QJsonObject toJson(const {{ dt.name }} &element)
{
QJsonObject obj;
{% for row in dt.cppMember %}
{{ row.ddsToJsonElement }};
{% endfor %}
return obj;
}
template<>
{{ dt.name }} fromJson(const QJsonValue &value, type<{{ dt.name }}>)
{
const QJsonObject objElement(value.toObject());
{{ dt.name }} element;
{% for row in dt.cppMember %}
{ {{ row.jsonElementToDds }}; } {% endfor %}
return element;
}
//
// extensions to support sequence of seqence of Non-POD :
//
QJsonValue toJson(const std::vector <{{ dt.name }} > &element)
{
if (element.size() == 0)
return QJsonValue();
QJsonArray array;
for(size_t i = 0; i < element.size(); ++i)
array.push_back(toJson(element.at(i)));
return array;
}
template<>
std::vector<{{ dt.name }}> fromJson(const QJsonValue &value, type< std::vector <{{ dt.name }} > >)
{
std::vector < {{ dt.name }} > ret;
if (value.isNull())
return ret;
if (!value.isArray())
return ret;
const QJsonArray array = value.toArray();
ret.reserve(array.size());
for(const auto &ele : array)
ret.push_back(fromJson(ele, type<{{ dt.name }}>{}));
return ret;
}
{% endfor %}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,50 +0,0 @@
/**
* Json <-> DDS conversion routines
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-11-30
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_json.h
#pragma once
#include "{{ namespace_abbr }}.hpp"
#include "{{ namespace_abbr }}_export.h"
#include <QJsonObject>
#include <QJsonValue>
namespace {{ namespace }}
{
template<typename T>
struct type{};
template<typename T>
T fromJson(const QJsonValue &val, type<T>);
template<typename T>
T fromJson(const QJsonValue &val)
{
return fromJson(val, type<T>{});
}
{% for dt in jsonTopics %}
// {{ dt.name }}
{{ namespace_abbr_upper }}_COMMON_EXPORT QJsonObject toJson(const {{ dt.name }} &element);
template<> {{ namespace_abbr_upper }}_COMMON_EXPORT {{ dt.name }} fromJson(const QJsonValue &val, type<{{ dt.name }}>);
{{ namespace_abbr_upper }}_COMMON_EXPORT QJsonValue toJson(const std::vector< {{ dt.name }} > &element);
template<> {{ namespace_abbr_upper }}_COMMON_EXPORT std::vector<{{ dt.name }}> fromJson(const QJsonValue &value, type< std::vector< {{ dt.name }} > >);
{% endfor %}
//
// extensions to support sequence of seqence of Non-POD :
//
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,919 +0,0 @@
/**
* RecordReplay Plugin
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-07-21
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_record_replay_plugin.cpp
#include "{{ namespace_abbr }}_record_replay_plugin.h"
#include <ngldb/nsqlquery.h>
#include <DDS/DTS/generic_dts_impl.h>
#include <DDS/DTC/generic_dtc_impl.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QtSql/QSqlError>
#include <LtsUtilsCore/dds_vector.h>
namespace {{ namespace }}
{
template<typename T>
struct type{};
// conversion DDS -> QVariant, encapsulate complex members into json
template<typename T>
QJsonValue toJson(const T &val);
template<typename T>
QVariant toQVariant(const T &val)
{
return QVariant::fromValue(val);
}
template<>
QJsonValue toJson(const int &vec)
{
return QJsonValue(vec);
}
template<>
QJsonValue toJson(const double &vec)
{
return QJsonValue(vec);
}
template<>
QJsonValue toJson(const qint64 &val)
{
return QJsonValue(QString::number(val));
}
template<>
QJsonValue toJson(const std::vector<int> &val)
{
if (val.size() == 0)
return QJsonValue();
QJsonArray array;
for(size_t i = 0; i < val.size(); ++i)
array.push_back(toJson(val.at(i)));
return QJsonValue(array);
}
template<typename T>
QVariant toQVariant(const dds::core::safe_enum<T> &val)
{
// do not use T here - otherwise the implicit ctor dds::core::safe_enum<T>() is used which leads to infinite recursion
return toQVariant<int>(val.underlying());
}
template<>
QJsonValue toJson(const std::string &element)
{
return QJsonValue(QString::fromStdString(element));
}
template<>
QVariant toQVariant(const std::string &val)
{
return QString::fromStdString(val);
}
template<typename T>
QVariant toQVariant(const std::vector<T> &data)
{
if (data.size() == 0)
return QString();
QJsonArray array;
for(size_t i = 0; i < data.size(); ++i)
array.push_back(toJson(data.at(i)));
return QString::fromUtf8(QJsonDocument(array).toJson());
}
template<>
QVariant toQVariant(const std::vector<uint8_t> &data)
{
if (data.size() == 0)
return QVariant();
return QVariant(LtsUtils::convert<QByteArray>(data));
}
template<typename T>
static QVariant toQVariant(const dds::core::optional<T> &val)
{
if (val.is_set())
return toQVariant(val.get());
return QVariant();
}
// conversion QVariant -> DDS, decode complex members from json
template<typename T>
T fromJson(const QJsonValue &val, type<T>);
template<>
int fromJson(const QJsonValue &val, type<int>)
{
return val.toInt();
}
template<>
double fromJson(const QJsonValue &val, type<double>)
{
return val.toDouble();
}
template<>
qint64 fromJson(const QJsonValue &val, type<qint64>)
{
return val.toString().toLongLong();
}
/*
template<>
std::vector<int> fromJson(const QJsonValue &val, type<std::vector<int>>)
{
std::vector<int> ret;
if (val.isNull())
return ret;
const QJsonArray array(QJsonDocument::fromJson(val.toString().toUtf8()).array());
ret.reserve(array.size());
for(const auto &ele : array)
ret.push_back(fromJson(ele, type<int>{} ));
return ret;
}
*/
template<typename T>
std::vector<T> fromJson(const QJsonValue &jsonData, type<std::vector<T>>)
{
std::vector<T> ret;
if (jsonData.isNull())
return ret;
const QJsonArray array(jsonData.toArray());
ret.reserve(array.size());
for(const auto &ele : array)
ret.push_back(fromJson(ele, type<T>{}));
return ret;
}
template<typename T>
std::vector<T> fromJson(const QVariant &jsonData, type<std::vector<T>>)
{
std::vector<T> ret;
if (jsonData.isNull())
return ret;
const QJsonArray array(QJsonDocument::fromJson(jsonData.toString().toUtf8()).array()); // warning: ugly and didn't work in the fromJson version above
ret.reserve(array.size());
for(const auto &ele : array)
ret.push_back(fromJson(ele, type<T>{}));
return ret;
}
// Primary fallback for any migrated plain-`enum class` enumerators that have no
// explicit specialization below - most importantly the nested foreign
// ProjectDataManagement / Core enums a plugin reads back from the database (they are
// not in this service's idl_enums, so the per-own-enum specializations do not cover
// them). Giving the primary template a body (rather than leaving it declaration-only)
// is what lets those foreign-enum instantiations resolve, so the plugin .so links. The
// explicit specializations for primitives / own enums / structs / vectors / optionals
// all take precedence over this fallback, so it is only ever instantiated for an enum
// type, where `static_cast<T>(val.toInt())` is the correct conversion.
template<typename T>
T fromQVariant(const QVariant &val, type<T>)
{
return static_cast<T>(val.toInt());
}
template<>
bool fromQVariant(const QVariant &val, type<bool>)
{
return val.toBool();
}
template<>
short fromQVariant(const QVariant &val, type<short>)
{
return static_cast<short>(val.toInt());
}
template<>
int fromQVariant(const QVariant &val, type<int>)
{
return val.toInt();
}
template<>
long long fromQVariant(const QVariant &val, type<long long>)
{
return val.toLongLong();
}
// rtiddsgen 4.7 spells the 64 bit integer members int64_t, which is `long` on LP64 and thus
// a different type from the Qt side's qint64 (`long long`). The tag dispatch above is keyed on
// the exact type, so the generated type needs its own conversion even though the two are the
// same width. GrantleeWrappers::cppDdsType() names the generated member type, so this is the
// overload the record replay code instantiates for those members.
template<>
int64_t fromQVariant(const QVariant &val, type<int64_t>)
{
return static_cast<int64_t>(val.toLongLong());
}
template<>
double fromQVariant(const QVariant &val, type<double>)
{
return val.toDouble();
}
template<>
std::string fromJson(const QJsonValue &val, type<std::string>)
{
return val.toString().toStdString();
}
template<>
std::string fromQVariant(const QVariant &val, type<std::string>)
{
return val.toString().toStdString();
}
template<typename T>
dds::core::safe_enum<T> fromQVariant(const QVariant &val, type<dds::core::safe_enum<T>>)
{
using inner = typename dds::core::safe_enum<T>::inner_enum;
return static_cast<inner>(val.toInt());
}
template<typename T>
std::vector<T> fromQVariant(const QVariant &val, type<std::vector<T>> t)
{
return fromJson(val, t);
}
template<>
std::vector<uint8_t> fromQVariant(const QVariant &in, type<std::vector<uint8_t>>)
{
return LtsUtils::convert<std::vector<uint8_t>>(in.toByteArray());
}
template<typename T>
static dds::core::optional<T> fromQVariant(const QVariant &val, type<dds::core::optional<T>>)
{
if (val.isNull())
return dds::core::optional<T>();
return fromQVariant(val, type<T>{});
}
template<typename T>
T fromJson(const QJsonValue &val)
{
return fromJson(val, type<T>{});
}
template<typename T>
T fromQVariant(const QVariant &val)
{
return fromQVariant(val, type<T>{});
}
// rtiddsgen 7.7 spells the migrated enumerators as plain `enum class` in the service
// namespace (the legacy dds::core::safe_enum<T> wrapper is gone). The record_replay
// plugin still instantiates fromQVariant<Enum>() for each enum member it reads back,
// and none of the generic overloads above are ever deduced for those types, so each of
// this service's own enumerations needs its own explicit specialization. `idl_enums`
// carries the service's own enumerations by local name and this block runs inside
// `namespace {{ namespace }}`, so the local name resolves to the fully-qualified enum
// type. (Nested foreign PDM enumerations are still not covered - same limitation as the
// documented svcBuildings exclusion.)
{% for enumeration in idl_enums %}
template<>
{{ enumeration.name }} fromQVariant(const QVariant &val, type<{{ enumeration.name }}>)
{
return static_cast<{{ enumeration.name }}>(val.toInt());
}
{% endfor %}
{% for dt in serializeTopics.member %}
//
// Begin {{ dt.typeFq }}
//
template<>
{{ dt.typeFq }} fromJson(const QJsonValue &val, type<{{ dt.typeFq }}>)
{
const QJsonObject objElement(val.toObject());
{{ dt.typeFq }} element;
{% for row in dt.recTable.cppMember %}
{ {{ row.jsonElementToDds }}; } {% endfor %}
return element;
}
template<>
QJsonValue toJson(const {{ dt.typeFq }} &element)
{
QJsonObject obj;
{% for row in dt.recTable.cppMember %}
{{ row.ddsToJsonElement }}; {% endfor %}
return obj;
}
template <>
QVariant toQVariant(const {{ dt.typeFq }} &val)
{
return QString::fromUtf8(QJsonDocument(toJson(val).toObject()).toJson());
}
template<>
{{ dt.typeFq }} fromQVariant(const QVariant &val, type<{{ dt.typeFq }}>)
{
return fromJson(QJsonDocument::fromJson(val.toString().toUtf8()).object(), type<{{ dt.typeFq }}>{});
}
//
// extensions to support sequence of seqence of Non-POD :
//
template<>
QJsonValue toJson(const std::vector < {{ dt.typeFq }} > &data)
{
if (data.size() == 0)
return QJsonValue();
QJsonArray array;
for(size_t i = 0; i < data.size(); ++i)
array.push_back(toJson(data.at(i)));
return array;
}
template<>
std::vector < {{ dt.typeFq }} > fromJson(const QJsonValue &val)
{
std::vector < {{ dt.typeFq }} > ret;
if (val.isNull())
return ret;
if (!val.isArray())
return ret;
const QJsonArray array = val.toArray();
ret.reserve(array.size());
for(const auto &ele : array)
ret.push_back(fromJson(ele, type<{{ dt.typeFq }}>{}));
return ret;
}
//
// : extensions to support sequence of seqence of Non-POD
//
//
// End {{ dt.typeFq }}
//
{% endfor %}
RecordReplayPlugin::RecordReplayPlugin()
: m_exerciseId(0)
, m_bCanHandleSchemasRead(true)
, m_bCanHandleSchemasWrite(true)
, m_logger(getLogger("RecordReplay"))
{}
RecordReplayPlugin::~RecordReplayPlugin() = default;
QString RecordReplayPlugin::getTableName(const QString &tableName, bool bCanHandleSchemas) const
{
if (!bCanHandleSchemas)
return tableName;
return DBCommon::getExerciseTableName(tableName, m_exerciseId);
}
bool RecordReplayPlugin::initDtc(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId)
{
m_ddsConnector = ddsConnector;
m_exerciseId = exerciseId;
if (!m_ddsConnector)
return false;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
m_dtc{{ dt.name }}.reset(m_ddsConnector->createDTC<DTC::{{ dt.name }}Impl>());
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::initDts(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId,
bool bCanHandleSchemas,
NSqlQuery &query)
{
Q_UNUSED(query);
m_ddsConnector = ddsConnector;
m_exerciseId = exerciseId;
m_bCanHandleSchemasRead = bCanHandleSchemas;
m_bCanHandleSchemasWrite = bCanHandleSchemas;
if (!m_ddsConnector)
return false;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
m_dts{{ dt.name }}.reset(m_ddsConnector->createDTS<DTS::{{ dt.name }}Impl>());
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::initCopy(int exerciseId, bool bCanHandleSchemasRead, bool bCanHandleSchemasWrite)
{
m_exerciseId = exerciseId;
m_bCanHandleSchemasRead = bCanHandleSchemasRead;
m_bCanHandleSchemasWrite = bCanHandleSchemasWrite;
return true;
}
bool RecordReplayPlugin::copyData(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
// {{ dt.name }}
if (!copyInitialData{{ dt.name }}(queryFrom, queryTo, fromTime)) return false;
if (!copyData{{ dt.name }}(queryFrom, queryTo, fromTime, toTime)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::ddsTopicsToDatabase(NSqlQuery &q, QHash<QString, qint64> &minRecordSampleTimes)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!write{{ dt.name }}(q, minRecordSampleTimes)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::setReplayStartTime(NSqlQuery &q, qint64 startTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!read{{ dt.name }}At(q, startTime)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!read{{ dt.name }}(q, fromTime, toTime)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::publishData(qint64 desiredTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!publish{{ dt.name }}(desiredTime)) return false;
{% endif %} {% endfor %}
return true;
}
void RecordReplayPlugin::clearData()
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
clearData{{ dt.name }}();
{% endif %} {% endfor %}
}
std::map<QString, std::vector<RecordReplayPlugin::RecSnapshot>> RecordReplayPlugin::getSnapshotTimes(NSqlQuery &q,
qint64 toTime,
qint64 interval,
const QHash<QString, qint64> &minRecordedSampleTimes)
{
std::map<QString, std::vector<RecordReplayPlugin::RecSnapshot>> ret;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
{
const QString &topicName(QStringLiteral("{{ dt.name }}"));
std::vector<RecordReplayPlugin::RecSnapshot> vec;
auto it = minRecordedSampleTimes.find(topicName);
if (it != minRecordedSampleTimes.end())
{
qint64 nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, it.value()); // is 0 or -1 if no samples are available
nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging
if (nextSampleTime != m_snapshot{{ dt.name }}MapTime)
{
// the current snapshot data does not match the starting time -> reread m_{{ dt.nameFlc }}Keys
readLastSnapshotKeysFor{{ dt.name }}(q, nextSampleTime);
//DEBUG_LOG(m_logger) << "fromTime: " << fromTime << ", m_snapshotParticipantMapTime: " << m_snapshotParticipantMapTime
// << " - rereading snapshot times! Map now contains " << m_snapshot{{ dt.name }}Map.size() << " entries";
}
while (nextSampleTime > 0 && nextSampleTime <= toTime)
{
const qint64 intervalStart = nextSampleTime;
const qint64 intervalEnd = intervalStart + interval;
updateSnapshotKeys{{ dt.name }}(q, intervalStart, intervalEnd);
vec.emplace_back(RecSnapshot(intervalEnd, m_snapshot{{ dt.name }}Map));
nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, intervalEnd);
nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging
}
}
if (!vec.empty())
ret.insert({topicName, std::move(vec)});
}
{% endif %} {% endfor %}
return ret;
}
QVector<QString> RecordReplayPlugin::getRecordTopicNames() const
{
return {{% for dt in dataTopics.member %} {% if dt.recordTopic %}
QStringLiteral("{{ dt.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %}
};
}
QVector<QString> RecordReplayPlugin::getRecordTableNames() const
{
return {{% for dt in dataTopics.member %} {% if dt.recordTopic %}
QStringLiteral("{{ dt.recTable.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %}
};
}
qint64 RecordReplayPlugin::getMinimumSampleTime(NSqlQuery &q, const QString &topicName, qint64 defaultValue) const
{
QString tableName;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (topicName == QLatin1String("{{ dt.name }}"))
tableName = QStringLiteral("{{ dt.recTable.name }}");
{% endif %} {% endfor %}
if (tableName.isEmpty())
return defaultValue;
const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") + tableName);
if (!q.execAndFetch(stmt))
return defaultValue;
const QVariant val = q.value(0);
return val.isNull() ? defaultValue : val.toLongLong();
}
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
bool RecordReplayPlugin::read{{ dt.name }}At(NSqlQuery &q, qint64 startTime)
{
// read snapshot data for {{ dt.name }}
if (!readLastSnapshotFor{{ dt.name }}(q, startTime)) return false;
// read other samples between snapshot time and startTime
if (!read{{ dt.name }}(q, m_snapshot{{ dt.name }}MapTime, startTime)) return false;
return true;
}
bool RecordReplayPlugin::write{{ dt.name }}(NSqlQuery &q, QHash<QString, qint64> &minRecordSampleTimes)
{
using DATA = {{ namespace }}::{{ dt.name }};
if (!m_dtc{{ dt.name }})
return false;
qint64 minRecordSampleTime = std::numeric_limits<qint64>::max();
if (m_dtc{{ dt.name }}->hasSamples())
{
TRACE_LOG(m_logger) << "m_dtc{{ dt.name }}->hasSamples() == true ";
const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) +
QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES "
" (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});"));
if (!q.prepare(stmt))
return false;
dds::sub::LoanedSamples<DATA> samples = m_dtc{{ dt.name }}->takeAlive();
TRACE_LOG(m_logger) << "samples count: " << samples.length();
for (const auto &sample : samples)
{
const DATA &data = sample.data();
INFO_LOG(m_logger) << "m_dtc{{ dt.name }} {{ dt.recTable.ddsKey.cppMemberName }}: " << data.{{ dt.recTable.ddsKey.cppMemberName }};
{% for row in dt.recTable.cppMember %}
q.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.ddsToQVariant }});
{% endfor %}
q.bindValue(QStringLiteral(":dds_is_alive"), true);
if (!q.exec())
return false;
// rtiddsgen 4.7 declares sample_time as int64_t (long), while the record
// boundary is qint64 (long long); std::min requires both arguments to be
// the same type, so narrow the generated member explicitly.
minRecordSampleTime = std::min(minRecordSampleTime, static_cast<qint64>(data.sampleTime));
}
}
dds::sub::LoanedSamples<DATA> disposedSamples = m_dtc{{ dt.name }}->takeDisposed();
if (disposedSamples.length() != 0)
{
const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) +
QStringLiteral(" ( dds_is_alive, sample_time, {{ dt.recTable.ddsKey.dbColumnName }}) VALUES "
" (:dds_is_alive,:sample_time,:{{ dt.recTable.ddsKey.dbColumnName }});"));
if (!q.prepare(stmt))
return false;
DEBUG_LOG(m_logger) << "disposedSamples count: " << disposedSamples.length();
auto dr = m_dtc{{ dt.name }}->dataReader()->dataReader();
{{ dt.name }} data;
for (const auto &sample : disposedSamples)
{
const qint64 sampleTime = sample.info().source_timestamp().to_millisecs();
dr.key_value(data, sample.info().instance_handle());
q.bindValue(QStringLiteral(":dds_is_alive"), false);
q.bindValue(QStringLiteral(":sample_time"), sampleTime);
q.bindValue(QStringLiteral(":{{ dt.recTable.ddsKey.cppMemberName }}"), {{ dt.recTable.ddsKey.ddsToQVariant }});
if (!q.exec())
return false;
minRecordSampleTime = std::min(minRecordSampleTime, sampleTime);
}
}
if (minRecordSampleTime != std::numeric_limits<qint64>::max())
minRecordSampleTimes[QStringLiteral("{{ dt.name }}")] = minRecordSampleTime;
return true;
}
// read the last snapshot which is <= startTime, set m_snapshot{{ dt.name }}MapTime to the snapshot time
bool RecordReplayPlugin::readLastSnapshotFor{{ dt.name }}(NSqlQuery &q, qint64 startTime)
{
// rebuild m_{{ dt.nameFlc }}Map content for given startTime
clearData{{ dt.name }}();
const QString stmt(QStringLiteral("SELECT id, time_stamp FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 "
" ORDER BY time_stamp DESC LIMIT 1").arg(startTime));
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}";
return false;
}
if (!q.next() || q.value(0).isNull())
{
INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}";
return true;
}
const qint64 idSnapshot = q.value(0).toLongLong();
const qint64 timeSnapshot = q.value(1).toLongLong();
const QString stmt2(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')'));
if (!q.exec(stmt2))
return false;
{{ namespace }}::{{ dt.name }} data;
while (q.next())
{
int i = 0;
const bool bIsAlive = q.value(i++).toBool();
{% for row in dt.recTable.viewMember %}
{{ row.qVariantToDds }};
{% endfor %}
if (bIsAlive)
m_{{ dt.nameFlc }}Map.insert(data.sampleTime, data);
else
m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime, data);
m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }});
}
m_snapshot{{ dt.name }}MapTime = timeSnapshot;
return true;
}
bool RecordReplayPlugin::read{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) +
QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';'));
if (!q.exec(stmt))
return false;
{{ namespace }}::{{ dt.name }} data;
while (q.next())
{
int i = 0;
const bool bIsAlive = q.value(i++).toBool();
{% for row in dt.recTable.viewMember %}
{{ row.qVariantToDds }};
{% endfor %}
if (bIsAlive)
m_{{ dt.nameFlc }}Map.insert(data.sampleTime, data);
else
m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime, data);
m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }});
}
return true;
}
bool RecordReplayPlugin::updateSnapshotKeys{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
const QString stmt(QStringLiteral("SELECT {{ dt.recTable.ddsKey.dbColumnName }}, {{ dt.recTable.primaryKey.dbColumnName }}, dds_is_alive FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime) +
QStringLiteral(" AND sample_time <= ") + QString::number(toTime) +
QStringLiteral(" ORDER BY sample_time "));
DEBUG_LOG(m_logger) << "stmt: " << stmt;
if (!q.exec(stmt))
return false;
while (q.next())
{
const qint64 key = q.value(0){{ dt.recTable.ddsKey.cppFromQvPost }};
const qint64 pk = q.value(1){{ dt.recTable.primaryKey.cppFromQvPost }};
const bool isAlive = q.value(2).toBool();
if (isAlive)
{
DEBUG_LOG(m_logger) << "INSERT {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive;
m_snapshot{{ dt.name }}Map[key] = pk;
m_{{ dt.nameFlc }}Keys.insert(key);
}
else
{
DEBUG_LOG(m_logger) << "REMOVE {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive;
m_snapshot{{ dt.name }}Map.erase(key);
m_{{ dt.nameFlc }}Keys.erase(key);
}
}
// new validity time for snapshot data
m_snapshot{{ dt.name }}MapTime = toTime;
DEBUG_LOG(m_logger) << "updateSnapshotKeys{{ dt.name }}(" << fromTime << ", " << toTime << ") = "
<< m_snapshot{{ dt.name }}Map.size();
return true;
}
bool RecordReplayPlugin::readLastSnapshotKeysFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime)
{
const QString stmt(QStringLiteral("SELECT rec_id_array FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 "
" ORDER BY time_stamp DESC LIMIT 1").arg(fromTime));
// no previous snapshot found?
if (!q.exec(stmt))
return false;
if (!q.next() || q.value(0).isNull())
{
DEBUG_LOG(m_logger) << "no snapshot entry for {{ dt.name }} found";
return true;
}
m_snapshot{{ dt.name }}Map = LtsUtils::BigintArray2D::fromPgString(q.value(0).toString())();
DEBUG_LOG(m_logger) << "Map size {{ dt.name }}: " << m_snapshot{{ dt.name }}Map.size();
return true;
}
qint64 RecordReplayPlugin::getNextSampleTimeFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime)
{
const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime));
qint64 ret = -1;
if (q.execAndFetch(stmt) && !q.value(0).isNull())
ret = q.value(0).toLongLong();
DEBUG_LOG(m_logger) << "next min sampleTime for {{ dt.name }} is " << ret << " (> " << fromTime << ")";
return ret;
}
bool RecordReplayPlugin::publish{{ dt.name }}(qint64 desiredTime)
{
if (!m_dts{{ dt.name }})
return false;
{
auto it = m_{{ dt.nameFlc }}Map.begin();
while (it != m_{{ dt.nameFlc }}Map.end() && it.key() <= desiredTime)
{
m_dts{{ dt.name }}->write(it.value());
it = m_{{ dt.nameFlc }}Map.erase(it);
}
}
{
auto it = m_{{ dt.nameFlc }}DisposedMap.begin();
while (it != m_{{ dt.nameFlc }}DisposedMap.end() && it.key() <= desiredTime)
{
m_dts{{ dt.name }}->remove(it.value());
it = m_{{ dt.nameFlc }}DisposedMap.erase(it);
}
}
return true;
}
void RecordReplayPlugin::clearData{{ dt.name }}()
{
if (m_dts{{ dt.name }})
{
{{ dt.name }} tmpData;
for (auto key : m_{{ dt.nameFlc }}Keys)
{
tmpData.{{ dt.key.name }} = key;
m_dts{{ dt.name }}->remove(tmpData);
}
}
m_{{ dt.nameFlc }}Keys.clear();
m_{{ dt.nameFlc }}Map.clear();
m_{{ dt.nameFlc }}DisposedMap.clear();
}
bool RecordReplayPlugin::copyInitialData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 startTime)
{
const QString stmt(QStringLiteral("SELECT id FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 "
" ORDER BY time_stamp DESC LIMIT 1").arg(startTime));
if (!queryFrom.exec(stmt))
{
ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}";
return false;
}
if (!queryFrom.next() || queryFrom.value(0).isNull())
{
INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}";
return true;
}
const qint64 idSnapshot = queryFrom.value(0).toLongLong();
const QString stmtSelect(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')'));
if (!queryFrom.exec(stmtSelect))
return false;
return copyDataPrepared{{ dt.name }}(queryFrom, queryTo);
}
bool RecordReplayPlugin::copyData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime)
{
const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) +
QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';'));
if (!queryFrom.exec(stmt))
return false;
return copyDataPrepared{{ dt.name }}(queryFrom, queryTo);
}
bool RecordReplayPlugin::copyDataPrepared{{ dt.name }}(NSqlQuery &queryFromPrepared, NSqlQuery &queryTo)
{
const QString stmtInsert(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) +
QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES "
" (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});"));
if (!queryTo.prepare(stmtInsert))
return false;
while (queryFromPrepared.next())
{
int i = 0;
queryTo.bindValue(QStringLiteral(":dds_is_alive"), queryFromPrepared.value(i++));
{% for row in dt.recTable.cppMember %}
queryTo.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), queryFromPrepared.value(i++));
{% endfor %}
if (!queryTo.exec())
return false;
}
return true;
}
{% endif %} {% endfor %}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,637 +0,0 @@
/**
* RecordReplay Plugin
* $Id: svc_record_replay_plugin.cpp.grantlee 50268 2020-03-06 07:42:09Z ehrlicher $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_07_assault_energy/Lts/Common/CodeGen/svc_record_replay_plugin.cpp.grantlee $
* $Author: ehrlicher $
* Created by Christian Ehrlicher, SLSE, 2016-07-21
*/
Leider gibnt es Probleme, wenn man Sequenzen von Sequenzen von Non-PODs erstellt.
Es erbgaben sich unresolved symbols... (Compiler war zufrieden, Linker aber nicht - hmm)
Mit dem folgenden Konstrukt konnten ich diese "beheben", doch je nach Konstellation in der XYZ_service.xml
ergaben sich Fehler a la "template specialication after instatiation"
Daher haben wir (zumindest zunächst) Abstand von dieser Erweiterung genommen.
...
{% for dt in serializeTopics.member %}
//
// Begin {{ dt.typeFq }}
//
template<>
{{ dt.typeFq }} fromJson(const QJsonValue &val, type<{{ dt.typeFq }}>)
{
const QJsonObject objElement(val.toObject());
{{ dt.typeFq }} element;
{% for row in dt.recTable.cppMember %}
{ {{ row.jsonElementToDds }}; } {% endfor %}
return element;
}
template<>
QJsonValue toJson(const {{ dt.typeFq }} &element)
{
QJsonObject obj;
{% for row in dt.recTable.cppMember %}
{{ row.ddsToJsonElement }}; {% endfor %}
return obj;
}
template <>
QVariant toQVariant(const {{ dt.typeFq }} &val)
{
return QString::fromUtf8(QJsonDocument(toJson(val).toObject()).toJson());
}
template<>
{{ dt.typeFq }} fromQVariant(const QVariant &val, type<{{ dt.typeFq }}>)
{
return fromJson(QJsonDocument::fromJson(val.toString().toUtf8()).object(), type<{{ dt.typeFq }}>{});
}
//
// extensions to support sequence of seqence of Non-POD :
//
template<>
QJsonValue toJson(const std::vector < {{ dt.typeFq }} > &data)
{
if (data.size() == 0)
return QJsonValue();
QJsonArray array;
for(size_t i = 0; i < data.size(); ++i)
array.push_back(toJson(data.at(i)));
return QJsonDocument(array).object();
}
template<>
std::vector < {{ dt.typeFq }} > fromJson(const QJsonValue &val)
{
std::vector < {{ dt.typeFq }} > ret;
if (val.isNull() == 0)
return ret;
if (val.isArray() == 0) return ret;
const QJsonArray array = val.toArray();
ret.reserve(array.size());
for(const auto &ele : array)
ret.push_back(fromJson(ele, type<{{ dt.typeFq }}>{}));
return ret;
}
//
// : extensions to support sequence of seqence of Non-POD
//
//
// End {{ dt.typeFq }}
//
{% endfor %}
RecordReplayPlugin::RecordReplayPlugin()
: m_exerciseId(0)
, m_bCanHandleSchemasRead(true)
, m_bCanHandleSchemasWrite(true)
, m_logger(getLogger("RecordReplay"))
{}
RecordReplayPlugin::~RecordReplayPlugin() = default;
QString RecordReplayPlugin::getTableName(const QString &tableName, bool bCanHandleSchemas) const
{
if (!bCanHandleSchemas)
return tableName;
return DBCommon::getExerciseTableName(tableName, m_exerciseId);
}
bool RecordReplayPlugin::initDtc(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId)
{
m_ddsConnector = ddsConnector;
m_exerciseId = exerciseId;
if (!m_ddsConnector)
return false;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
m_dtc{{ dt.name }}.reset(m_ddsConnector->createDTC<DTC::{{ dt.name }}Impl>());
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::initDts(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId,
bool bCanHandleSchemas,
NSqlQuery &query)
{
Q_UNUSED(query);
m_ddsConnector = ddsConnector;
m_exerciseId = exerciseId;
m_bCanHandleSchemasRead = bCanHandleSchemas;
m_bCanHandleSchemasWrite = bCanHandleSchemas;
if (!m_ddsConnector)
return false;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
m_dts{{ dt.name }}.reset(m_ddsConnector->createDTS<DTS::{{ dt.name }}Impl>());
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::initCopy(int exerciseId, bool bCanHandleSchemasRead, bool bCanHandleSchemasWrite)
{
m_exerciseId = exerciseId;
m_bCanHandleSchemasRead = bCanHandleSchemasRead;
m_bCanHandleSchemasWrite = bCanHandleSchemasWrite;
return true;
}
bool RecordReplayPlugin::copyData(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
// {{ dt.name }}
if (!copyInitialData{{ dt.name }}(queryFrom, queryTo, fromTime)) return false;
if (!copyData{{ dt.name }}(queryFrom, queryTo, fromTime, toTime)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::ddsTopicsToDatabase(NSqlQuery &q, QHash<QString, qint64> &minRecordSampleTimes)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!write{{ dt.name }}(q, minRecordSampleTimes)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::setReplayStartTime(NSqlQuery &q, qint64 startTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!read{{ dt.name }}At(q, startTime)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!read{{ dt.name }}(q, fromTime, toTime)) return false;
{% endif %} {% endfor %}
return true;
}
bool RecordReplayPlugin::publishData(qint64 desiredTime)
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (!publish{{ dt.name }}(desiredTime)) return false;
{% endif %} {% endfor %}
return true;
}
void RecordReplayPlugin::clearData()
{
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
clearData{{ dt.name }}();
{% endif %} {% endfor %}
}
std::map<QString, std::vector<RecordReplayPlugin::RecSnapshot>> RecordReplayPlugin::getSnapshotTimes(NSqlQuery &q,
qint64 toTime,
qint64 interval,
const QHash<QString, qint64> &minRecordedSampleTimes)
{
std::map<QString, std::vector<RecordReplayPlugin::RecSnapshot>> ret;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
{
const QString &topicName(QStringLiteral("{{ dt.name }}"));
std::vector<RecordReplayPlugin::RecSnapshot> vec;
auto it = minRecordedSampleTimes.find(topicName);
if (it != minRecordedSampleTimes.end())
{
qint64 nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, it.value()); // is 0 or -1 if no samples are available
nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging
if (nextSampleTime != m_snapshot{{ dt.name }}MapTime)
{
// the current snapshot data does not match the starting time -> reread m_{{ dt.nameFlc }}Keys
readLastSnapshotKeysFor{{ dt.name }}(q, nextSampleTime);
//DEBUG_LOG(m_logger) << "fromTime: " << fromTime << ", m_snapshotParticipantMapTime: " << m_snapshotParticipantMapTime
// << " - rereading snapshot times! Map now contains " << m_snapshot{{ dt.name }}Map.size() << " entries";
}
while (nextSampleTime > 0 && nextSampleTime <= toTime)
{
const qint64 intervalStart = nextSampleTime;
const qint64 intervalEnd = intervalStart + interval;
updateSnapshotKeys{{ dt.name }}(q, intervalStart, intervalEnd);
vec.emplace_back(RecSnapshot(intervalEnd, m_snapshot{{ dt.name }}Map));
nextSampleTime = getNextSampleTimeFor{{ dt.name }}(q, intervalEnd);
nextSampleTime -= (nextSampleTime % interval); // sync to interval - only needed for better debugging
}
}
if (!vec.empty())
ret.insert({topicName, std::move(vec)});
}
{% endif %} {% endfor %}
return ret;
}
QVector<QString> RecordReplayPlugin::getRecordTopicNames() const
{
return {{% for dt in dataTopics.member %} {% if dt.recordTopic %}
QStringLiteral("{{ dt.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %}
};
}
QVector<QString> RecordReplayPlugin::getRecordTableNames() const
{
return {{% for dt in dataTopics.member %} {% if dt.recordTopic %}
QStringLiteral("{{ dt.recTable.name }}"){% if not forloop.last %}, {% endif %} {% endif %} {% endfor %}
};
}
qint64 RecordReplayPlugin::getMinimumSampleTime(NSqlQuery &q, const QString &topicName, qint64 defaultValue) const
{
QString tableName;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
if (topicName == QLatin1String("{{ dt.name }}"))
tableName = QStringLiteral("{{ dt.recTable.name }}");
{% endif %} {% endfor %}
if (tableName.isEmpty())
return defaultValue;
const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") + tableName);
if (!q.execAndFetch(stmt))
return defaultValue;
const QVariant val = q.value(0);
return val.isNull() ? defaultValue : val.toLongLong();
}
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
bool RecordReplayPlugin::read{{ dt.name }}At(NSqlQuery &q, qint64 startTime)
{
// read snapshot data for {{ dt.name }}
if (!readLastSnapshotFor{{ dt.name }}(q, startTime)) return false;
// read other samples between snapshot time and startTime
if (!read{{ dt.name }}(q, m_snapshot{{ dt.name }}MapTime, startTime)) return false;
return true;
}
bool RecordReplayPlugin::write{{ dt.name }}(NSqlQuery &q, QHash<QString, qint64> &minRecordSampleTimes)
{
using DATA = {{ namespace }}::{{ dt.name }};
if (!m_dtc{{ dt.name }})
return false;
qint64 minRecordSampleTime = std::numeric_limits<qint64>::max();
if (m_dtc{{ dt.name }}->hasSamples())
{
TRACE_LOG(m_logger) << "m_dtc{{ dt.name }}->hasSamples() == true ";
const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) +
QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES "
" (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});"));
if (!q.prepare(stmt))
return false;
dds::sub::LoanedSamples<DATA> samples = m_dtc{{ dt.name }}->takeAlive();
TRACE_LOG(m_logger) << "samples count: " << samples.length();
for (const auto &sample : samples)
{
const DATA &data = sample.data();
INFO_LOG(m_logger) << "m_dtc{{ dt.name }} {{ dt.recTable.ddsKey.cppMemberName }}: " << data.{{ dt.recTable.ddsKey.cppMemberName }}();
{% for row in dt.recTable.cppMember %}
q.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), {{ row.ddsToQVariant }});
{% endfor %}
q.bindValue(QStringLiteral(":dds_is_alive"), true);
if (!q.exec())
return false;
minRecordSampleTime = std::min(minRecordSampleTime, data.sampleTime());
}
}
dds::sub::LoanedSamples<DATA> disposedSamples = m_dtc{{ dt.name }}->takeDisposed();
if (disposedSamples.length() != 0)
{
const QString stmt(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) +
QStringLiteral(" ( dds_is_alive, sample_time, {{ dt.recTable.ddsKey.dbColumnName }}) VALUES "
" (:dds_is_alive,:sample_time,:{{ dt.recTable.ddsKey.dbColumnName }});"));
if (!q.prepare(stmt))
return false;
DEBUG_LOG(m_logger) << "disposedSamples count: " << disposedSamples.length();
auto dr = m_dtc{{ dt.name }}->dataReader()->dataReader();
{{ dt.name }} data;
for (const auto &sample : disposedSamples)
{
const qint64 sampleTime = sample.info().source_timestamp().to_millisecs();
dr.key_value(data, sample.info().instance_handle());
q.bindValue(QStringLiteral(":dds_is_alive"), false);
q.bindValue(QStringLiteral(":sample_time"), sampleTime);
q.bindValue(QStringLiteral(":{{ dt.recTable.ddsKey.cppMemberName }}"), {{ dt.recTable.ddsKey.ddsToQVariant }});
if (!q.exec())
return false;
minRecordSampleTime = std::min(minRecordSampleTime, sampleTime);
}
}
if (minRecordSampleTime != std::numeric_limits<qint64>::max())
minRecordSampleTimes[QStringLiteral("{{ dt.name }}")] = minRecordSampleTime;
return true;
}
// read the last snapshot which is <= startTime, set m_snapshot{{ dt.name }}MapTime to the snapshot time
bool RecordReplayPlugin::readLastSnapshotFor{{ dt.name }}(NSqlQuery &q, qint64 startTime)
{
// rebuild m_{{ dt.nameFlc }}Map content for given startTime
clearData{{ dt.name }}();
const QString stmt(QStringLiteral("SELECT id, time_stamp FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 "
" ORDER BY time_stamp DESC LIMIT 1").arg(startTime));
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}";
return false;
}
if (!q.next() || q.value(0).isNull())
{
INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}";
return true;
}
const qint64 idSnapshot = q.value(0).toLongLong();
const qint64 timeSnapshot = q.value(1).toLongLong();
const QString stmt2(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')'));
if (!q.exec(stmt2))
return false;
{{ namespace }}::{{ dt.name }} data;
while (q.next())
{
int i = 0;
const bool bIsAlive = q.value(i++).toBool();
{% for row in dt.recTable.viewMember %}
{{ row.qVariantToDds }};
{% endfor %}
if (bIsAlive)
m_{{ dt.nameFlc }}Map.insert(data.sampleTime(), data);
else
m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime(), data);
m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }}());
}
m_snapshot{{ dt.name }}MapTime = timeSnapshot;
return true;
}
bool RecordReplayPlugin::read{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) +
QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';'));
if (!q.exec(stmt))
return false;
{{ namespace }}::{{ dt.name }} data;
while (q.next())
{
int i = 0;
const bool bIsAlive = q.value(i++).toBool();
{% for row in dt.recTable.viewMember %}
{{ row.qVariantToDds }};
{% endfor %}
if (bIsAlive)
m_{{ dt.nameFlc }}Map.insert(data.sampleTime(), data);
else
m_{{ dt.nameFlc }}DisposedMap.insert(data.sampleTime(), data);
m_{{ dt.nameFlc }}Keys.insert(data.{{ dt.key.name }}());
}
return true;
}
bool RecordReplayPlugin::updateSnapshotKeys{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
const QString stmt(QStringLiteral("SELECT {{ dt.recTable.ddsKey.dbColumnName }}, {{ dt.recTable.primaryKey.dbColumnName }}, dds_is_alive FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime) +
QStringLiteral(" AND sample_time <= ") + QString::number(toTime) +
QStringLiteral(" ORDER BY sample_time "));
DEBUG_LOG(m_logger) << "stmt: " << stmt;
if (!q.exec(stmt))
return false;
while (q.next())
{
const qint64 key = q.value(0){{ dt.recTable.ddsKey.cppFromQvPost }};
const qint64 pk = q.value(1){{ dt.recTable.primaryKey.cppFromQvPost }};
const bool isAlive = q.value(2).toBool();
if (isAlive)
{
DEBUG_LOG(m_logger) << "INSERT {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive;
m_snapshot{{ dt.name }}Map[key] = pk;
m_{{ dt.nameFlc }}Keys.insert(key);
}
else
{
DEBUG_LOG(m_logger) << "REMOVE {{ dt.name }} key: " << key << ", pk: " << pk << ", isAlive: " << isAlive;
m_snapshot{{ dt.name }}Map.erase(key);
m_{{ dt.nameFlc }}Keys.erase(key);
}
}
// new validity time for snapshot data
m_snapshot{{ dt.name }}MapTime = toTime;
DEBUG_LOG(m_logger) << "updateSnapshotKeys{{ dt.name }}(" << fromTime << ", " << toTime << ") = "
<< m_snapshot{{ dt.name }}Map.size();
return true;
}
bool RecordReplayPlugin::readLastSnapshotKeysFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime)
{
const QString stmt(QStringLiteral("SELECT rec_id_array FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 "
" ORDER BY time_stamp DESC LIMIT 1").arg(fromTime));
// no previous snapshot found?
if (!q.exec(stmt))
return false;
if (!q.next() || q.value(0).isNull())
{
DEBUG_LOG(m_logger) << "no snapshot entry for {{ dt.name }} found";
return true;
}
m_snapshot{{ dt.name }}Map = LtsUtils::BigintArray2D::fromPgString(q.value(0).toString())();
DEBUG_LOG(m_logger) << "Map size {{ dt.name }}: " << m_snapshot{{ dt.name }}Map.size();
return true;
}
qint64 RecordReplayPlugin::getNextSampleTimeFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime)
{
const QString stmt(QStringLiteral("SELECT MIN(sample_time) FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > ") + QString::number(fromTime));
qint64 ret = -1;
if (q.execAndFetch(stmt) && !q.value(0).isNull())
ret = q.value(0).toLongLong();
DEBUG_LOG(m_logger) << "next min sampleTime for {{ dt.name }} is " << ret << " (> " << fromTime << ")";
return ret;
}
bool RecordReplayPlugin::publish{{ dt.name }}(qint64 desiredTime)
{
if (!m_dts{{ dt.name }})
return false;
{
auto it = m_{{ dt.nameFlc }}Map.begin();
while (it != m_{{ dt.nameFlc }}Map.end() && it.key() <= desiredTime)
{
m_dts{{ dt.name }}->write(it.value());
it = m_{{ dt.nameFlc }}Map.erase(it);
}
}
{
auto it = m_{{ dt.nameFlc }}DisposedMap.begin();
while (it != m_{{ dt.nameFlc }}DisposedMap.end() && it.key() <= desiredTime)
{
m_dts{{ dt.name }}->remove(it.value());
it = m_{{ dt.nameFlc }}DisposedMap.erase(it);
}
}
return true;
}
void RecordReplayPlugin::clearData{{ dt.name }}()
{
if (m_dts{{ dt.name }})
{
{{ dt.name }} tmpData;
for (auto key : m_{{ dt.nameFlc }}Keys)
{
tmpData.{{ dt.key.name }}(key);
m_dts{{ dt.name }}->remove(tmpData);
}
}
m_{{ dt.nameFlc }}Keys.clear();
m_{{ dt.nameFlc }}Map.clear();
m_{{ dt.nameFlc }}DisposedMap.clear();
}
bool RecordReplayPlugin::copyInitialData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 startTime)
{
const QString stmt(QStringLiteral("SELECT id FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE svc_name = '{{ namespace_abbr}}' AND topic_name = '{{ dt.name }}' AND time_stamp <= %1 "
" ORDER BY time_stamp DESC LIMIT 1").arg(startTime));
if (!queryFrom.exec(stmt))
{
ERROR_LOG(m_logger) << "Error while fetch max(time_stamp) for topic {{ dt.name }}";
return false;
}
if (!queryFrom.next() || queryFrom.value(0).isNull())
{
INFO_LOG(m_logger) << "No snapshot available for topic {{ dt.name }}";
return true;
}
const qint64 idSnapshot = queryFrom.value(0).toLongLong();
const QString stmtSelect(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE rec_id IN (SELECT reduce_dim2(rec_id_array) FROM ") + getTableName(QStringLiteral("rec_snapshot"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE id = ") + QString::number(idSnapshot) + QLatin1Char(')'));
if (!queryFrom.exec(stmtSelect))
return false;
return copyDataPrepared{{ dt.name }}(queryFrom, queryTo);
}
bool RecordReplayPlugin::copyData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime)
{
const QString stmt(QStringLiteral("SELECT dds_is_alive, {% for row in dt.recTable.viewMember %}{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %} FROM ") +
getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasRead) +
QStringLiteral(" WHERE sample_time > " ) + QString::number(fromTime) +
QStringLiteral(" AND sample_time <= ") + QString::number(toTime) + QLatin1Char(';'));
if (!queryFrom.exec(stmt))
return false;
return copyDataPrepared{{ dt.name }}(queryFrom, queryTo);
}
bool RecordReplayPlugin::copyDataPrepared{{ dt.name }}(NSqlQuery &queryFromPrepared, NSqlQuery &queryTo)
{
const QString stmtInsert(QStringLiteral("INSERT INTO ") + getTableName(QStringLiteral("{{ dt.recTable.name }}"), m_bCanHandleSchemasWrite) +
QStringLiteral(" ( dds_is_alive, {% for row in dt.recTable.cppMember %} {{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %}) VALUES "
" (:dds_is_alive, {% for row in dt.recTable.cppMember %}:{{ row.dbColumnName }}{% if not forloop.last %}, {% endif %}{% endfor %});"));
if (!queryTo.prepare(stmtInsert))
return false;
while (queryFromPrepared.next())
{
int i = 0;
queryTo.bindValue(QStringLiteral(":dds_is_alive"), queryFromPrepared.value(i++));
{% for row in dt.recTable.cppMember %}
queryTo.bindValue(QStringLiteral(":{{ row.dbColumnName }}"), queryFromPrepared.value(i++));
{% endfor %}
if (!queryTo.exec())
return false;
}
return true;
}
{% endif %} {% endfor %}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,128 +0,0 @@
/**
* RecordReplay Plugin
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-07-21
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_record_replay_plugin.h
#pragma once
#include <QMultiMap>
#include <QVector>
#include <set>
#include <DDS/DDSConnector/dds_connector.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_types.h>
#include <{{ include_prefix }}/{{ namespace_abbr }}_dts_types.h>
#include <{{ include_prefix }}/logger.h>
// Resolved from the Lts include root (see include_directories(BEFORE .../Lts)
// in the root CMakeLists); the old <../../Common/...> spelling only worked
// for in-source builds.
#include <Common/PluginInterfaces/record_replay_plugin.h>
#include <LtsUtilsCore/pgarray.h>
{% if custom_record_replay %}
#ifndef {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT
# else
# if defined(MAKE_{{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_LIB)
# define {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT Q_DECL_EXPORT
# else
# define {{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
{% endif %}
namespace {{ namespace }} {
class {% if custom_record_replay %}{{ namespace_abbr_upper }}RECORDREPLAYPLUGIN_EXPORT{% endif %}
RecordReplayPlugin {% if not custom_record_replay %}final{% endif %}: public QObject, public RecordReplay::RecordReplayPlugin
{
Q_OBJECT
{% if not custom_record_replay %}
Q_PLUGIN_METADATA(IID "special:3:recordreplay.{{ namespace_abbr }}")
{% endif %}
Q_INTERFACES(RecordReplay::RecordReplayPlugin)
public:
RecordReplayPlugin();
~RecordReplayPlugin();
QString pluginName() const override { return QStringLiteral("{{ namespace_abbr }}RecordReplayPlugin"); }
bool initDtc(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId) override;
bool initDts(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId,
bool bCanHandleSchemas,
NSqlQuery &query) override;
bool initCopy(int exerciseId, bool bCanHandleSchemasRead, bool bCanHandleSchemasWrite) override;
bool copyData(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime) override;
QString svcShortName() const override { return QStringLiteral("{{ namespace_abbr }}"); }
// recording functions
bool ddsTopicsToDatabase(NSqlQuery &q, QHash<QString, qint64> &minRecordSampleTimes) override;
// publishing functions
bool setReplayStartTime(NSqlQuery &q, qint64 startTime) override;
bool ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime) override;
bool publishData(qint64 desiredTime) override;
void clearData() override;
// structure/functions for snapshot creation
std::map<QString, std::vector<RecSnapshot>> getSnapshotTimes(NSqlQuery &query,
qint64 toTime,
qint64 interval,
const QHash<QString, qint64> &minRecordedSampleTimes) override;
QVector<QString> getRecordTopicNames() const override;
QVector<QString> getRecordTableNames() const override;
qint64 getMinimumSampleTime(NSqlQuery &q, const QString &topicName, qint64 defaultValue) const override;
// getter for initial service startup
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
// {{ dt.name }}
QMultiMap<qint64, {{ include_prefix }}::{{ dt.name }}> &get{{ dt.name }}Map() { return m_{{ dt.nameFlc }}Map; }
const QMultiMap<qint64, {{ include_prefix }}::{{ dt.name }}> &get{{ dt.name }}Map() const { return m_{{ dt.nameFlc }}Map; }
bool read{{ dt.name }}At(NSqlQuery &q, qint64 startTime);
{% endif %} {% endfor %}
protected:
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
// {{ dt.name }}
bool write{{ dt.name }}(NSqlQuery &q, QHash<QString, qint64> &minRecordSampleTimes);
bool read{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime);
bool readLastSnapshotFor{{ dt.name }}(NSqlQuery &q, qint64 startTime);
bool updateSnapshotKeys{{ dt.name }}(NSqlQuery &q, qint64 fromTime, qint64 toTime);
bool readLastSnapshotKeysFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime);
qint64 getNextSampleTimeFor{{ dt.name }}(NSqlQuery &q, qint64 fromTime);
bool publish{{ dt.name }}(qint64 desiredTime);
void clearData{{ dt.name }}();
bool copyInitialData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 startTime);
bool copyData{{ dt.name }}(NSqlQuery &queryFrom, NSqlQuery &queryTo, qint64 fromTime, qint64 toTime);
bool copyDataPrepared{{ dt.name }}(NSqlQuery &queryFromPrepared, NSqlQuery &queryTo);
{% endif %} {% endfor %}
QString getTableName(const QString &tableName, bool bCanHandleSchemas) const;
protected:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
int m_exerciseId;
bool m_bCanHandleSchemasRead;
bool m_bCanHandleSchemasWrite;
{% for dt in dataTopics.member %} {% if dt.recordTopic %}
// {{ dt.name }}
std::unique_ptr<DTC::{{ dt.name }}> m_dtc{{ dt.name }};
std::unique_ptr<DTS::{{ dt.name }}> m_dts{{ dt.name }};
QMultiMap<qint64, {{ include_prefix }}::{{ dt.name }}> m_{{ dt.nameFlc }}Map;
QMultiMap<qint64, {{ include_prefix }}::{{ dt.name }}> m_{{ dt.nameFlc }}DisposedMap;
std::set<qint64> m_{{ dt.nameFlc }}Keys;
std::map<int64_t,int64_t> m_snapshot{{ dt.name }}Map;
int64_t m_snapshot{{ dt.name }}MapTime = -1; // time for which the snapshot is valid
{% endif %} {% endfor %}
NGL::INGLLogger& m_logger;
};
}
{% endautoescape %}
@@ -1,102 +0,0 @@
/**
* DDS Type Plugin to provide custom types to other applications like ddsview
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-07-08
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_topic_type_plugin.cpp
#include "{{ namespace_abbr }}_topic_type_plugin.h"
#include <{{ namespace_abbr }}_dtc_types.h>
#include <{{ namespace_abbr }}_ftc_types.h>
#include <{{ namespace_abbr }}_fts_types.h>
#include <DDS/FTC/generic_ftc_impl.h>
#include <DDS/FTS/generic_fts_impl.h>
#include <DDS/Routing/routing_plugin.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace {{ namespace }}
{
QMap<std::string, DDSGeneric::TopicDescription> TopicTypePlugin::getDataTopics() const
{
return
{{% for dt in dataTopics.member %}
{
dds::topic::topic_type_name<{{ dt.name }}>::value(),
{
DDS::Helper::getShortTopicName(dds::topic::topic_type_name<{{ dt.name }}>::value()),
dds::topic::topic_type_name<{{ dt.name }}>::value(),
rti::topic::dynamic_type<{{ dt.name }}>::get()
}
}{% if not forloop.last %}, {% endif %}{% endfor %}
};
}
QMap<std::string, DDSGeneric::TopicDescription> TopicTypePlugin::getFunctionTopics() const
{
return
{{% for ft in functionTopics.member %}
{
FTC::TopicNames<FTC::{{ ft.name }}>::requestTopicName(),
{
FTC::TopicNames<FTC::{{ ft.name }}>::requestTopicName(),
dds::topic::topic_type_name<FTC::{{ ft.name }}::RequestTopic>::value(),
rti::topic::dynamic_type<FTC::{{ ft.name }}::RequestTopic>::get()
}
},
{
FTC::TopicNames<FTC::{{ ft.name }}>::replyTopicName(),
{
FTC::TopicNames<FTC::{{ ft.name }}>::replyTopicName(),
dds::topic::topic_type_name<FTC::{{ ft.name }}::ReplyTopic>::value(),
rti::topic::dynamic_type<FTC::{{ ft.name }}::ReplyTopic>::get()
}
}{% if not forloop.last %}, {% endif %}{% endfor %}
};
}
inline NGL::INGLLogger &logger(const char *name)
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(name);
}
std::vector<RoutingService::TopicDispatcherInterface *> TopicTypePlugin::topicsToRouteToWs(IDL::PartitionType partitionType) const
{
if (partitionType == IDL::PartitionType::Replay)
return
{{% for dt in dataTopics.topicsToRouteInReplay %}
new RoutingService::DataTopicDispatcher<{{ dt.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ dt.name }}")),{% endfor %}
{% for ft in functionTopics.topicsToRouteInReplay %}
new RoutingService::FunctionTopicReplyDispatcher<FTS::{{ ft.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Reply")){% if not forloop.last %},{% endif %}{% endfor %}
};
return
{{% for dt in dataTopics.topicsToRouteToClient %}
new RoutingService::DataTopicDispatcher<{{ dt.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ dt.name }}")),{% endfor %}
{% for ft in functionTopics.member %}
new RoutingService::FunctionTopicReplyDispatcher<FTS::{{ ft.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Reply")){% if not forloop.last %},{% endif %}{% endfor %}
};
}
std::vector<RoutingService::TopicDispatcherInterface *> TopicTypePlugin::topicsToRouteToSv(IDL::PartitionType partitionType) const
{
if (partitionType == IDL::PartitionType::Replay)
return
{{% for ft in functionTopics.topicsToRouteInReplay %}
new RoutingService::FunctionTopicRequestDispatcher<FTC::{{ ft.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Request")){% if not forloop.last %},{% endif %}{% endfor %}
};
return
{{% for dt in dataTopics.topicsToRouteToServer %}
new RoutingService::DataTopicDispatcher<{{ dt.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ dt.name }}")),{% endfor %}
{% for ft in functionTopics.member %}
new RoutingService::FunctionTopicRequestDispatcher<FTC::{{ ft.name }}>(logger("RSC.{{ namespace_abbr_upper }}.{{ ft.name }}.Request")){% if not forloop.last %},{% endif %}{% endfor %}
};
}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,41 +0,0 @@
/**
* DDS Type Plugin to provide custom types to other applications like ddsview
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-07-08
*/
#pragma once
{% autoescape off %}
// filename: {{ namespace_abbr }}_topic_type_plugin.h
#include <{{ include_prefix }}/{{ namespace_abbr }}.hpp>
#include <Common/ProjectConfig/project_defines.h>
#include <QtCore/QObject>
// Resolved from the Lts include root (see include_directories(BEFORE .../Lts)
// in the root CMakeLists); the old <../../DDS/...> spelling only worked for
// in-source builds because moc/gcc do not resolve it relative to this file.
#include <DDS/Generic/topic_type_plugin.h>
namespace {{ namespace }}
{
class TopicTypePlugin final : public QObject, public DDSGeneric::TopicTypePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "topic_type:3:{{ namespace_abbr }}")
Q_INTERFACES(DDSGeneric::TopicTypePlugin)
public:
TopicTypePlugin() = default;
QMap<std::string, DDSGeneric::TopicDescription> getDataTopics() const override final;
QMap<std::string, DDSGeneric::TopicDescription> getFunctionTopics() const override final;
std::vector<RoutingService::TopicDispatcherInterface *> topicsToRouteToWs(IDL::PartitionType partitionType) const override final;
std::vector<RoutingService::TopicDispatcherInterface *> topicsToRouteToSv(IDL::PartitionType partitionType) const override final;
QString getServiceShortName() const override final { return QStringLiteral("{{ namespace_abbr_upper }}"); }
};
}
{% endautoescape %}
@@ -1,43 +0,0 @@
/**
* @RDE_HEADER@
* Convert DSS enumerations to translatable strings
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
// filename: {{ namespace_abbr }}_translation.cpp
#include "{{ namespace_abbr }}_translation.h"
#include <QCoreApplication>
namespace {{ namespace }}
{
{% for idl_enum in idl_enums %}
//
// {{ idl_enum.name }}
//
QString toString({{ idl_enum.name }} state)
{
switch(state)
{{% for idl_member in idl_enum.member %}
case {{ idl_enum.name }}::{{ idl_member.key }}: return QCoreApplication::translate("{{ idl_enum.name }}", "{{ idl_member.key }}");
{% endfor %}
default: break;
}
return QCoreApplication::translate("{{ idl_enum.name }}", "Unknown (%1)").arg(static_cast<int>(state));
}
{% endfor %}
{% for dt in dataTopics.member %}{% if dt.hasTrColumns %}{% for tr in dt.trColumns %}
// {{ dt.name }}::{{ tr.name }}
QString tr{{ dt.name }}{{ tr.nameCc }}(const std::string &text)
{
return QCoreApplication::translate("{{ dt.name }}", text.c_str(), "{{ tr.name }}");
}
{% endfor %}{% endif %}{% endfor %}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,32 +0,0 @@
/**
* @RDE_HEADER@
* Convert DSS enumerations to translatable strings
* $Id$
* $HeadURL$
* $Author$
*/
{% autoescape off %}
#pragma once
// filename: {{ namespace_abbr }}_translation.h
#include <string>
#include <{{ include_prefix }}/{{ namespace_abbr }}_enums.hpp>
#include <{{ include_prefix }}/{{ namespace_abbr }}_export.h>
class QString;
namespace {{ namespace }}
{
// Translatable enumerations:
{% for idl_enum in idl_enums %}
{{ namespace_abbr_upper }}_COMMON_EXPORT QString toString({{ idl_enum.name }} state);{% endfor %}
// Translatable struct members:
{% for dt in dataTopics.member %}{% if dt.hasTrColumns %}{% for tr in dt.trColumns %}
{{ namespace_abbr_upper }}_COMMON_EXPORT QString tr{{ dt.nameCc }}{{ tr.nameCc }}(const std::string &text); // {{ dt.nameCc }}::{{ tr.name }} {% endfor %}{% endif %}{% endfor %}
} // namespace {{ namespace }}
{% endautoescape %}
@@ -1,70 +0,0 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <ktexttemplate/engine.h>
#include <ktexttemplate/templateloader.h>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QString templatesDir = QDir::currentPath();
if (argc > 1)
{
templatesDir = QString::fromLocal8Bit(argv[1]);
}
KTextTemplate::Engine engine;
auto loader = QSharedPointer<KTextTemplate::FileSystemTemplateLoader>::create();
loader->setTemplateDirs(QStringList() << templatesDir);
engine.addTemplateLoader(loader);
QDir d(templatesDir);
QStringList files = d.entryList(QStringList() << QStringLiteral("*.grantlee"), QDir::Files);
if (files.isEmpty())
{
qDebug() << "No .grantlee templates found in" << templatesDir;
// Treat absence as successful parse (nothing to check) to avoid failing
// when directory is empty in different setups.
return 0;
}
bool ok = true;
for (const QString& f : files)
{
KTextTemplate::Template t;
try
{
t = engine.loadByName(f);
}
catch (...)
{
qDebug() << "Exception while loading" << f;
ok = false;
continue;
}
if (!t)
{
qDebug() << "Failed to load template" << f;
ok = false;
continue;
}
if (t->error() != KTextTemplate::NoError)
{
qDebug() << "Parse error in" << f << ":" << t->errorString();
ok = false;
continue;
}
// Render with empty context to ensure rendering path also works.
KTextTemplate::Context ctx;
QString out = t->render(&ctx);
if (out.isNull())
{
qDebug() << "Rendering produced null for" << f;
ok = false;
continue;
}
}
return ok ? 0 : 2;
}
@@ -1,32 +0,0 @@
#include "app.h"
#include "AppChooser/app_chooser_model.h"
#include "ui_app.h"
namespace LtsAppManager
{
CustomViewApp::CustomViewApp(QWidget* parent) : AbstractDisplayWidget(parent), m_ui(new Ui::CustomViewApp)
{
m_ui->setupUi(this);
m_ui->image->setMenuEnabled(false);
}
CustomViewApp::~CustomViewApp()
{
delete m_ui;
}
bool CustomViewApp::init(const QModelIndex& idx)
{
if (!idx.isValid())
return false;
m_ui->lbAppName->setText(rowData(idx, AppChooserModel::Name).toString());
return true;
}
} // namespace LtsAppManager
@@ -1,28 +0,0 @@
#pragma once
#include <QtCore/QObject>
#include <Viewer/LtsDataView/abstract_display_widget.h>
namespace Ui
{
class CustomViewApp;
}
namespace LtsAppManager
{
class CustomViewApp final : public LtsDataView::AbstractDisplayWidget
{
Q_OBJECT
public:
explicit CustomViewApp(QWidget* parent = nullptr);
~CustomViewApp();
bool init(const QModelIndex& idx) override;
private:
Ui::CustomViewApp* m_ui;
};
} // namespace LtsAppManager
@@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomViewApp</class>
<widget class="QWidget" name="CustomViewApp">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>150</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="LtsWidgets::LtsImageEditor" name="image">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="lbAppName">
<property name="text">
<string notr="true">&lt;App Name&gt;</string>
</property>
</widget>
</item>
<item>
<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>
</layout>
</item>
<item>
<widget class="QLabel" name="lbDescription">
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&amp;lt;Description with wordwrap&amp;gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsImageEditor</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_image_editor.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -1,80 +0,0 @@
#include "AppChooser/app_chooser.h"
#include "AppChooser/CustomViews/app.h"
#include "AppChooser/app_chooser_model.h"
#include "lts_app_manager.h"
#include "ui_app_chooser.h"
#include <QtGui/QCloseEvent>
#include <QtCore/QDebug>
namespace LtsAppManager
{
AppChooser::AppChooser() : QWidget(nullptr), m_ui(new Ui::AppChooser), m_model(), m_madeSelection(false)
{
m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
connect(m_ui->widget, &LtsDataView::LtsDataView::LtsDataView::signalDoubleClicked, this,
&AppChooser::slotAppSelected);
connect(m_ui->widget, &LtsDataView::LtsDataView::LtsDataView::signalEnterPressed, this,
&AppChooser::slotAppSelected);
connect(m_ui->widget, &LtsDataView::LtsDataView::LtsDataView::signalSelectionChanged, this, &AppChooser::enaDisa);
connect(m_ui->pbQuit, &QPushButton::clicked, this, &QWidget::close);
connect(m_ui->pbSelect, &QPushButton::clicked, this, &AppChooser::slotAppSelected);
m_model = new AppChooserModel(this);
m_model->initModel(LtsAppManager::getInstance()->getAvailableApps());
m_ui->widget->seamless();
m_ui->widget->setModel(m_model);
m_ui->widget->hideColumn(AppChooserModel::Columns::Visible);
}
AppChooser::~AppChooser()
{
delete m_ui;
}
void AppChooser::slotQuit()
{
if (m_alreadyCloseEmitted)
return;
m_alreadyCloseEmitted = true;
emit signalLogout();
}
void AppChooser::closeEvent(QCloseEvent*)
{
if (!m_madeSelection)
slotQuit();
}
void AppChooser::slotAppSelected()
{
const QModelIndex idx = m_ui->widget->currentIndex();
const LtsAppDescriptor::AppDescription app = m_model->getRowData(idx.row());
if (!app.active)
return;
hide();
m_madeSelection = true;
emit signalAppSelected(app.id);
close();
}
void AppChooser::enaDisa()
{
const QModelIndexList selectedRows = m_ui->widget->selectedRows();
const int cntSelectedRows = selectedRows.size();
m_ui->pbSelect->setEnabled(cntSelectedRows == 1);
}
} // namespace LtsAppManager
@@ -1,45 +0,0 @@
#pragma once
#include <LtsAppDescriptor/lts_app_descriptor.h>
#include <QtCore/QObject>
#include <QtWidgets/QWidget>
namespace Ui
{
class AppChooser;
}
namespace LtsAppManager
{
class AppChooserModel;
class AppChooser : public QWidget
{
Q_OBJECT
public:
AppChooser();
~AppChooser();
private:
Ui::AppChooser* m_ui;
AppChooserModel* m_model;
bool m_madeSelection;
bool m_alreadyCloseEmitted{false};
void enaDisa();
protected:
void closeEvent(QCloseEvent*);
signals:
void signalAppSelected(LtsAppDescriptor::App appId);
void signalLogout();
private slots:
void slotAppSelected();
void slotQuit();
};
} // namespace LtsAppManager
@@ -1,92 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AppChooser</class>
<widget class="QWidget" name="AppChooser">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>400</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>App Chooser</string>
</property>
<property name="windowIcon">
<iconset resource="../../LtsWidgets/resources/lts_resources.qrc">
<normaloff>:/rde_icon_256.png</normaloff>:/rde_icon_256.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Select App:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsDataView::LtsDataView" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pbQuit">
<property name="text">
<string>Quit</string>
</property>
</widget>
</item>
<item>
<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>
<widget class="QPushButton" name="pbSelect">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header location="global">Viewer/LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../LtsWidgets/resources/lts_resources.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,48 +0,0 @@
#include "app_chooser_model.h"
#include <LtsSession/lts_session.h>
namespace LtsAppManager
{
AppChooserModel::AppChooserModel(QObject* parent)
: LtsWidgets::LtsAbstractTableModel<LtsAppDescriptor::AppDescription>({tr("Name")}, parent)
{
}
bool AppChooserModel::initModel(const LtsAppDescriptor::AppDescriptionVector& availableApps)
{
for (auto i = availableApps.constBegin(); i != availableApps.constEnd(); ++i)
{
const LtsAppDescriptor::AppDescription& appDesc = *i;
if (!appDesc.visible)
continue;
insertRow(appDesc);
}
return true;
}
QVariant AppChooserModel::data(const QModelIndex& index, int role) const
{
if (role != Qt::DisplayRole || !indexIsValid(index))
return QVariant();
const LtsAppDescriptor::AppDescription& ad = m_vecData.at(index.row());
switch (index.column())
{
case Name:
return LtsSession::LtsSession::appToString(ad.id);
case Visible:
return ad.visible;
}
return QVariant();
}
} // namespace LtsAppManager
@@ -1,25 +0,0 @@
#pragma once
#include <LtsAppDescriptor/lts_app_descriptor.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <QtCore/QObject>
namespace LtsAppManager
{
class AppChooserModel final : public LtsWidgets::LtsAbstractTableModel<LtsAppDescriptor::AppDescription>
{
Q_OBJECT
public:
enum Columns {
Name = 0,
Visible,
};
explicit AppChooserModel(QObject* parent = nullptr);
bool initModel(const LtsAppDescriptor::AppDescriptionVector& availableApps);
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override final;
};
} // namespace LtsAppManager
@@ -1,71 +0,0 @@
# Make ${Qt6_Gui} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui)
set(Qt6_Gui ${Qt6_Gui})
else()
set(Qt6_Gui "")
endif()
# Make ${Qt6_Widgets} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets)
set(Qt6_Widgets ${Qt6_Widgets})
else()
set(Qt6_Widgets "")
endif()
#
# $Id$
# $HeadURL$
# $Author$
# Created by Mike Wille, SLSE, 2016-10-27
#
# LtsAppManager
set(LtsAppManager_SRCS
lts_app_manager.cpp
AppChooser/app_chooser.cpp
AppChooser/app_chooser_model.cpp
AppChooser/CustomViews/app.cpp
)
NGL_WRAP_UI(LtsAppManager_SRCS
AppChooser/app_chooser.ui
AppChooser/CustomViews/app.ui
)
IF(BUILD_RTI)
NGL_ADD_LIBRARY2(WS LtsAppManager SHARED ${LtsAppManager_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppManager
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_SOURCE_DIR}/AppChooser/
)
if(QT6_GUI_AVAILABLE)
NGL_TARGET_LINK_LIBRARIES(LtsAppManager
PRIVATE
NGLLogger
LtsDataView
LtsWidgets
LtsLogin
LtsPluginManager
LtsSession
LtsSessionData
LtsUtilsSettings
LtsMultiMonitor
ProjectConfig
${Qt6_Gui}
${Qt6_Widgets}
Qt6::Network # debug server
OpmSettingsGuiPluginInterface
LtsPluginLoader
PUBLIC
Qt6::Core
LtsAppDescriptor
)
endif()
NGL_INSTALL_TARGET(LtsAppManager)
ENDIF(BUILD_RTI)
@@ -1,963 +0,0 @@
#include "lts_app_manager.h"
#include "AppChooser/app_chooser.h"
#include <LtsLogin/lts_login.h>
#include <LtsMultiMonitor/lts_multi_monitor.h>
#include <LtsPluginManager/lts_plugin_manager.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsSession/lts_session_manager.h>
#include <LtsUtilsSettings/settings.h>
#include <LtsWidgets/lts_events.h>
#include <LtsWidgets/lts_global_wait_dialog.h>
#include <OperatorManagement/opm_settings_gui_plugin_interface.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
#include <QtWidgets/QMessageBox>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtCore/QTimer>
#include <QtCore/QUuid>
#include <utility>
#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 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_jenkins[] = JENKINS_BUILD_NUMBER;
namespace LtsAppManager
{
struct LtsAppManagerHandler
{
LtsAppManager ltsAppManager;
};
Q_GLOBAL_STATIC(LtsAppManagerHandler, s_ltsAppManager)
LtsAppManager* LtsAppManager::getInstance()
{
return &s_ltsAppManager->ltsAppManager;
}
LtsAppManager::LtsAppManager() : m_login(nullptr)
{
qRegisterMetaType<LtsAppDescriptor::App>("LtsAppDescriptor::App");
slotStartDebugServer();
connect(qApp, &QCoreApplication::aboutToQuit, this, [this] {
delete m_login;
m_login = nullptr;
});
}
LtsAppManager::~LtsAppManager()
{
delete m_login;
m_login = nullptr;
}
void LtsAppManager::setAvailableApps(const LtsAppDescriptor::AppDescriptionVector& availableApps)
{
m_availableApps = availableApps;
#ifndef LTS_DEBUG
if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(
QStringLiteral("PluginManager.CheckAppRequirements")) == QVariant(true))
{
QMessageBox::information(nullptr, QStringLiteral("LtsAppManager"),
QStringLiteral("Checking App Requirements now"));
#else
{
#endif
QStringList errors;
LtsPluginManager::LtsPluginLoader::getInstance()->checkPlugins(availableApps, errors);
if (!errors.isEmpty())
QMessageBox::critical(
nullptr, QStringLiteral("LtsAppManager"),
QStringLiteral("App Requirements check failed!<br> - %1").arg(errors.join(QStringLiteral("<br> - "))));
}
if (m_availableApps.isEmpty())
{
QMessageBox::critical(nullptr, tr("App Manager"),
tr("There are no apps available for this project.\nPlease contact Support."));
abort();
}
}
void LtsAppManager::slotStartDebugServer()
{
QTcpServer* debugServer = new QTcpServer(this);
if (!debugServer->listen(QHostAddress::Any, 20000))
{
debugServer->deleteLater();
const int waitTryAgain = 5;
#ifdef LTS_DEBUG
#endif
QTimer::singleShot(waitTryAgain * 1000, this, &LtsAppManager::slotStartDebugServer);
return;
}
connect(debugServer, &QTcpServer::newConnection, this, [this]() {
QTcpServer* server = qobject_cast<QTcpServer*>(sender());
if (server == nullptr)
return;
QTcpSocket* client = server->nextPendingConnection();
if (client == nullptr)
return;
connect(client, &QTcpSocket::readyRead, this, [this]() {
QTcpSocket* client = qobject_cast<QTcpSocket*>(sender());
if (client == nullptr)
return;
QString commandStr(
QString::fromUtf8(client->readLine(100)).remove(QStringLiteral("\r")).remove(QStringLiteral("\n")));
QStringList argList = commandStr.split(QLatin1Char(' '), Qt::SkipEmptyParts);
if (argList.count() < 1)
return;
const QString& command = argList.at(0);
LtsSessionData::LtsSessionData* sessionData = LtsSessionData::LtsSessionData::getInstance();
static QStringList commands;
commands << QStringLiteral("showSessions") << QStringLiteral("operator") << QStringLiteral("help")
<< QStringLiteral("logout") << QStringLiteral("flag") << QStringLiteral("setFlag")
<< QStringLiteral("start") << QStringLiteral("voiceCommand") << QStringLiteral("ftcmock");
switch (commands.indexOf(command))
{
case 0: {
sessionData->setDebugFlagValue(QStringLiteral("DeveloperTools.ShowSessionsOnce"), true);
client->write("K");
break;
}
case 1: {
QString operatorName(sessionData->getOperatorName());
if (operatorName.isEmpty())
operatorName = QStringLiteral("(none)");
QString result(QStringLiteral("%1").arg(operatorName));
client->write(result.toUtf8());
break;
}
case 2: {
QString result(QStringLiteral("Available Commands: %1").arg(commands.join(QStringLiteral(", "))));
client->write(result.toUtf8());
break;
}
case 3: {
client->write("...");
slotLogout();
client->write("\r\nK");
break;
}
case 4: {
if (argList.count() != 2)
{
client->write("Usage: flag <flag name>");
break;
}
const QString& debugFlagName = argList.at(1);
if (!sessionData->hasDebugFlag(debugFlagName))
{
client->write("(unknown)");
break;
}
client->write(sessionData->debugFlagValue(argList.at(1)).toString().toUtf8());
break;
}
case 5: {
if (argList.count() < 3)
{
client->write("Usage: setFlag <flag name> <flag type> <value>\r\nFlag types: int, string (no "
"spaces allowed yet), boolean, double");
break;
}
static QStringList flagTypes;
flagTypes << QStringLiteral("int") << QStringLiteral("string") << QStringLiteral("boolean")
<< QStringLiteral("double");
const QString& flagType = argList.at(2);
bool k = false;
const QString& flagName = argList.at(1);
switch (flagTypes.indexOf(flagType))
{
case 0: {
int value = argList.at(3).toInt(&k);
if (k)
sessionData->setDebugFlagValue(flagName, value);
else
client->write("cannot convert value to integer");
break;
}
case 1: {
sessionData->setDebugFlagValue(flagName, argList.at(3));
break;
}
case 2: {
QString value(argList.at(3).toLower().simplified());
if (value == QStringLiteral("true"))
sessionData->setDebugFlagValue(flagName, true);
else if (value == QStringLiteral("false"))
sessionData->setDebugFlagValue(flagName, false);
else
client->write("cannot convert value to boolean");
break;
}
case 3: {
double value = argList.at(3).toDouble(&k);
if (k)
sessionData->setDebugFlagValue(flagName, value);
else
client->write("cannot convert value to double");
break;
}
default: {
client->write("unknown flag type");
break;
}
}
break;
}
case 6: {
if (argList.count() < 3)
{
client->write("Usage: start <app id> <domain id> ");
break;
}
client->write("not implemented yet");
break;
}
case 7: {
if (argList.count() < 2)
{
client->write("Usage: voiceCommand <keyword> <optional description>");
break;
}
static QStringList keywords;
keywords << QStringLiteral("AddMarker") << QStringLiteral("SetMarker")
<< QStringLiteral("AddSnapshot") << QStringLiteral("SaveSnapshot");
const QString& keyword = argList.at(1);
switch (keywords.indexOf(keyword))
{
case 0: {
auto ev = new LtsWidgets::AddMarkerEvent;
ev->setAccepted(false);
sessionData->setDebugFlagValue(command, keyword);
QCoreApplication::postEvent(qApp, ev);
break;
}
case 1: {
auto ev = new LtsWidgets::SetMarkerEvent(QString());
if (argList.size() > 2)
ev->setDescription(argList.at(2));
ev->setAccepted(false);
sessionData->setDebugFlagValue(command, keyword);
QCoreApplication::postEvent(qApp, ev);
break;
}
case 2: {
auto ev = new LtsWidgets::AddSnapshotEvent;
ev->setAccepted(false);
sessionData->setDebugFlagValue(command, keyword);
QCoreApplication::postEvent(qApp, ev);
break;
}
case 3: {
auto ev = new LtsWidgets::SaveSnapshotEvent(QString());
if (argList.size() > 2)
ev->setDescription(argList.at(2));
ev->setAccepted(false);
sessionData->setDebugFlagValue(command, keyword);
QCoreApplication::postEvent(qApp, ev);
break;
}
default:
break;
}
if (!sessionData->hasDebugFlag(command))
{
client->write("(unknown)");
break;
}
client->write(sessionData->debugFlagValue(argList.at(1)).toString().toUtf8());
break;
}
case 8: {
auto usage = [client]() {
client->write("Usage: ftcmock start <sessionId> <min ms> <max ms> <timer count> - See 'Session "
"id' in DevDialog "
"\nftcmock stop <sessionId>");
};
if (argList.count() < 3)
{
usage();
break;
}
bool k = false;
switch (QStringList({QStringLiteral("start"), QStringLiteral("stop")}).indexOf(argList.at(1)))
{
case 0: {
if (argList.count() < 6)
{
usage();
return;
}
const int min = argList.at(3).toInt(&k);
if (!k || min < 0)
{
client->write("min must be a positive integer!");
return;
}
const int max = argList.at(4).toInt(&k);
if (!k || max < 0)
{
client->write("max must be a positive integer!");
return;
}
const int timers = argList.at(5).toInt(&k);
if (!k || timers < 1)
{
client->write("'timer count' must be a positive integer and greater than 0!");
return;
}
break;
}
case 1: {
break;
}
default: {
usage();
return;
}
}
const int sessionId = argList.at(2).toInt(&k);
if (!k || sessionId < 0)
{
client->write("sessionId must be a positive integer!");
return;
}
emit signalCommand(client, argList);
break;
}
default: {
QString result(QStringLiteral("Unknown command '%1'").arg(command));
client->write(result.toUtf8());
break;
}
}
client->write("\r\n> ");
});
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));
QString motd(QStringLiteral("\r\nOperatorApplication - Version %1").arg(versionString));
client->write(motd.toUtf8());
client->write("\r\n> ");
});
}
void LtsAppManager::showLogin()
{
QTimer::singleShot(0, this, &LtsAppManager::slotShowLogin);
}
void LtsAppManager::slotShowLogin()
{
if (m_login == nullptr)
{
m_login = new LtsLogin::LtsLogin;
connect(m_login, &LtsLogin::LtsLogin::signalLoginDone, this, [&] {
#ifdef LTS_DEBUG
#endif
if (m_appList.isEmpty())
if (!startAppOrLetUserChoose(m_app, {}))
QTimer::singleShot(100, this, &LtsAppManager::slotLogout);
});
connect(m_login, &LtsLogin::LtsLogin::signalLogoffDone, this, &LtsAppManager::signalLogoffDone);
connect(m_login, &LtsLogin::LtsLogin::signalLogoffDone, this, &LtsAppManager::slotShowLogin,
Qt::QueuedConnection);
connect(m_login, &LtsLogin::LtsLogin::signalLogout, this, &LtsAppManager::slotLogout);
m_login->setLoginPlugin(m_loginPluginIid);
}
m_login->login();
}
bool LtsAppManager::hasRightForApp(const LtsAppDescriptor::AppDescription& appDescription) const
{
const QSet<int>& acls = LtsSessionData::LtsSessionData::getInstance()->getOperatorRights();
for (int reqRight : appDescription.requiredAcls)
{
if (!acls.contains(reqRight))
{
QMessageBox::critical(
nullptr, tr("App Manager"),
tr("The minimal required operator rights are not met. Therefore, the app cannot be started."));
return false;
}
}
return true;
}
void LtsAppManager::taskSwitch(QObject* _caller)
{
LtsSession::LtsSession* caller = qobject_cast<LtsSession::LtsSession*>(_caller);
if (caller == nullptr)
{
Q_ASSERT(false);
return;
}
QVector<LtsSession::LtsSession*> sessions;
for (const LtsPluginManager::LtsPluginManager* app : m_appList)
sessions += app->sessions();
if (sessions.isEmpty())
return;
int sessionCount = sessions.count();
if (sessionCount < 2)
return;
auto sessionIt = std::find_if(sessions.begin(), sessions.end(),
[caller](const LtsSession::LtsSession* session) { return (session == caller); });
if (sessionIt == sessions.end())
{
return;
}
auto it = ++sessionIt;
int x = 0;
do
{
if (x > 100)
{
return;
}
x++;
if (it == sessions.end())
{
if (sessions.begin() == sessionIt)
{
return;
}
it = sessions.begin();
}
LtsSession::LtsSession* session = *it;
QWidget* blw = session->baseLayoutWidget();
if (blw == nullptr || session->isForeverInvisible() || session == caller)
{
continue;
}
caller->baseLayoutWidget()->hide();
blw->showFullScreen();
return;
}
while (++it != sessionIt);
}
void LtsAppManager::hideAllOtherBLWs(QObject* _caller)
{
if (!LtsSessionData::LtsSessionData::getInstance()->isTablet())
return;
LtsSession::LtsSession* caller = qobject_cast<LtsSession::LtsSession*>(_caller);
QVector<LtsSession::LtsSession*> sessions;
for (const LtsPluginManager::LtsPluginManager* app : m_appList)
sessions += app->sessions();
for (LtsSession::LtsSession* session : sessions)
{
QWidget* blw = session->baseLayoutWidget();
if (blw != nullptr && !session->isForeverInvisible() && session != caller)
{
blw->hide();
}
}
if (caller != nullptr)
return;
for (LtsSession::LtsSession* session : sessions)
{
QWidget* blw = session->baseLayoutWidget();
if (blw == nullptr || session->isForeverInvisible())
{
continue;
}
blw->showFullScreen();
return;
}
}
bool LtsAppManager::startAppOrLetUserChoose(LtsAppDescriptor::App appId,
const LtsSession::DdsConnectionParams& connParams, bool force)
{
return startAppOrLetUserChoose(getApp(appId), connParams, force);
}
bool LtsAppManager::startAppOrLetUserChoose(const LtsAppDescriptor::AppDescription& appDescription,
const LtsSession::DdsConnectionParams& connParams, bool force)
{
if (!appDescription.active && !force)
{
AppChooser* appChooser = new AppChooser();
appChooser->setAttribute(Qt::WA_DeleteOnClose);
connect(appChooser, &AppChooser::signalLogout, this, &LtsAppManager::slotLogout);
connect(
appChooser, &AppChooser::signalAppSelected, this,
[this, connParams](LtsAppDescriptor::App appId) {
if (createApp(appId, connParams) == nullptr)
QTimer::singleShot(100, this, &LtsAppManager::slotLogout);
},
Qt::QueuedConnection);
appChooser->show();
return true;
}
if (createApp(appDescription, connParams, {}, false, force) == nullptr)
{
QMessageBox::critical(nullptr, tr("App Manager"), tr("Could not create app.\nPlease contact support."));
return false;
}
return true;
}
LtsPluginManager::LtsPluginManager* LtsAppManager::createApp(LtsAppDescriptor::App appId,
const LtsSession::DdsConnectionParams& connParams,
const QString& workspaceId, bool hidden, bool force)
{
return createApp(getApp(appId), connParams, workspaceId, hidden, force);
}
LtsPluginManager::LtsPluginManager* LtsAppManager::createApp(const LtsAppDescriptor::AppDescription& appDescription,
const LtsSession::DdsConnectionParams& connParams,
const QString& workspaceId, bool hidden, bool force)
{
if (!appDescription.isValid())
{
return nullptr;
}
if (!hasRightForApp(appDescription) && !force)
return nullptr;
const bool bDesiredAppHasSituationMapInstance =
LtsPluginManager::LtsPluginManager::hasSituationMapInstance(appDescription, true);
if (bDesiredAppHasSituationMapInstance)
{
const int curInstances = getNumberOfSituationMapInstances();
if (curInstances >= 3 && !force)
{
if (QMessageBox::Yes !=
QMessageBox::question(nullptr, tr("App Manager"),
tr("Are you sure you want to launch another resource-intensive app? <br />"
"Please note that you are currently running three apps containing the "
"resource-intensive map componment. <br />"
"It may lead to potential performance issues.")))
{
return nullptr;
}
}
}
auto* pluginManager = new LtsPluginManager::LtsPluginManager();
connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalQuit, this, &LtsAppManager::slotQuit);
connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalSoftQuit, this, &LtsAppManager::slotSoftQuit);
connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalLogout, this, &LtsAppManager::slotLogout);
connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalInitializationFinished, this,
&LtsAppManager::signalInitializationFinished);
connect(pluginManager, &LtsPluginManager::LtsPluginManager::destroyed, this, &LtsAppManager::slotAppDestroyed);
connect(pluginManager, &LtsPluginManager::LtsPluginManager::hideAllOtherBLWs, this,
&LtsAppManager::hideAllOtherBLWs);
connect(pluginManager, &LtsPluginManager::LtsPluginManager::taskSwitch, this, &LtsAppManager::taskSwitch);
#ifdef LTS_DEBUG
connect(pluginManager, &LtsPluginManager::LtsPluginManager::signalStillAlive, this, [this]() {
auto* app = qobject_cast<LtsPluginManager::LtsPluginManager*>(sender());
if (!app)
return;
for (auto i = m_appList.constBegin(); i != m_appList.constEnd(); ++i)
{
if (*i == app)
return;
}
});
#endif
if (!pluginManager->startApp(appDescription, false, connParams, workspaceId, hidden))
{
QMessageBox::critical(
nullptr, tr("App Manager"),
tr("The Plugin Manager failed to start the app due to plugin errors.\nPlease contact support."));
pluginManager->deleteLater();
return nullptr;
}
m_appList.append(pluginManager);
return pluginManager;
}
bool LtsAppManager::hasApp(LtsAppDescriptor::App appId) const
{
return (std::find_if(m_availableApps.constBegin(), m_availableApps.constEnd(),
[appId](const LtsAppDescriptor::AppDescription& app) {
bool ret = (app.id == appId);
return ret;
}) != m_availableApps.constEnd());
}
int LtsAppManager::getNumberOfSituationMapInstances() const
{
int numberOfSituationMapInstances = 0;
for (const LtsPluginManager::LtsPluginManager* app : std::as_const(m_appList))
{
if (!app)
continue;
if (app->hasSituationMapInstance())
++numberOfSituationMapInstances;
}
return numberOfSituationMapInstances;
}
LtsAppDescriptor::AppDescription LtsAppManager::getApp(LtsAppDescriptor::App appId) const
{
for (const LtsAppDescriptor::AppDescription& app : m_availableApps)
{
if (app.id == appId)
return app;
}
return LtsAppDescriptor::AppDescription();
}
void LtsAppManager::setMainApp(LtsAppDescriptor::App appId)
{
m_app = getApp(appId);
}
void LtsAppManager::saveState()
{
if (LtsSessionData::LtsSessionData::getInstance()->getOperatorId() == 0)
return;
QStringList unsavedWorkspaces =
LtsUtils::OperatorSettings::getInstance()->identifiers(QStringLiteral("UnsavedWorkspace"));
for (auto i = unsavedWorkspaces.constBegin(); i != unsavedWorkspaces.constEnd(); ++i)
LtsUtils::OperatorSettings::getInstance()->removeValue(QStringLiteral("UnsavedWorkspace"), *i);
QStringList unsavedFilterPreset =
LtsUtils::OperatorSettings::getInstance()->identifiers(QStringLiteral("UnsavedFilterPreset"));
for (auto i = unsavedFilterPreset.constBegin(); i != unsavedFilterPreset.constEnd(); ++i)
LtsUtils::OperatorSettings::getInstance()->removeValue(QStringLiteral("UnsavedFilterPreset"), *i);
QJsonArray root;
for (const LtsPluginManager::LtsPluginManager* app : std::as_const(m_appList))
{
const QVector<LtsSession::LtsSession*> sessions = app->sessions();
const auto appId = app->appDescription().id;
if (appId == LtsAppDescriptor::App::Login || sessions.count() < 1)
continue;
QJsonObject appState;
appState[QStringLiteral("id")] = static_cast<int>(appId);
QJsonArray sessionStates;
for (LtsSession::LtsSession* session : sessions)
{
if (session->baseLayout() != nullptr && session->baseLayout()->isSecondaryWidget())
continue;
QJsonObject sessionState;
int screenId = session->screenId();
int screenContainerId = session->screenContainerId();
sessionState[QStringLiteral("screenId")] = screenId;
sessionState[QStringLiteral("screenContainerId")] = screenContainerId;
sessionState[QStringLiteral("exerciseId")] = session->exerciseId();
sessionState[QStringLiteral("partitionId")] = session->partitionId();
sessionState[QStringLiteral("partitionType")] = int(session->partitionType());
sessionState[QStringLiteral("savegame")] = session->getSavegame();
const QString newId(QUuid::createUuid().toString());
sessionState[QStringLiteral("workspaceId")] = newId;
std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> ifcOpm;
if (session->getForeignGuiInterface<OperatorManagement::ForeignSettingsGuiInterface>(ifcOpm))
{
ifcOpm->callWorkspaceSaveUnsaved(newId);
}
sessionStates.append(sessionState);
}
if (!sessionStates.isEmpty())
{
appState[QStringLiteral("sessions")] = sessionStates;
root.append(appState);
}
}
QJsonDocument doc(root);
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("app"), QStringLiteral("LastAppStates"),
doc.toJson(QJsonDocument::Compact));
}
void LtsAppManager::slotLogout()
{
for (LtsPluginManager::LtsPluginManager* app : std::as_const(m_appList))
{
const QVector<LtsSession::LtsSession*> sessions = app->sessions();
for (LtsSession::LtsSession* session : sessions)
{
if (session->notifyUnsavedChanges() == false)
return;
}
}
saveState();
const QVector<LtsPluginManager::LtsPluginManager*> apps = m_appList;
if (apps.isEmpty() && m_login != nullptr)
m_login->logoff();
for (LtsPluginManager::LtsPluginManager* app : apps)
app->deleteLater();
}
void LtsAppManager::slotAppDestroyed()
{
LtsPluginManager::LtsPluginManager* app = static_cast<LtsPluginManager::LtsPluginManager*>(sender());
m_appList.removeOne(app);
if (m_appList.isEmpty() && m_login != nullptr)
{
m_login->logoff();
}
}
void LtsAppManager::slotQuit()
{
QObject* object = sender();
if (!object)
qCritical()
<< "ERROR: LtsAppManager: slot called without object - so it cannot be deleted from the app assignments!";
LtsPluginManager::LtsPluginManager* app = qobject_cast<LtsPluginManager::LtsPluginManager*>(object);
if (app == nullptr)
{
qCritical() << "ERROR: LtsAppManager: quit requested for invalid app";
return;
}
if (m_appList.count() == 1)
{
m_appList.removeAll(app);
saveState();
}
app->deleteLater();
}
void LtsAppManager::slotSoftQuit()
{
QObject* object = sender();
LtsPluginManager::LtsPluginManager* app = qobject_cast<LtsPluginManager::LtsPluginManager*>(object);
if (app == nullptr)
{
qCritical() << "ERROR: LtsAppManager: quit requested for invalid app";
return;
}
app->_softQuit();
if (m_appList.count() == 1)
{
m_appList.removeAll(app);
saveState();
}
}
bool LtsAppManager::otherSessionsResponsibility(LtsSession::LtsSession* thisSession,
const QVector<LtsAppDescriptor::App>& requiredAppTypes,
const QVector<IDL::PartitionType>& requiredPartitionTypes,
const QString& menuGroup, const QString& identifier,
LtsWidgets::LayoutArea area)
{
const QVector<LtsPluginManager::LtsPluginManager*> apps = LtsAppManager::LtsAppManager::getInstance()->getAppList();
QVector<LtsPluginManager::LtsPluginManager*> worthyApps;
for (LtsPluginManager::LtsPluginManager* app : apps)
{
if (!requiredAppTypes.contains(static_cast<LtsAppDescriptor::App>(app->appDescription().id)))
continue;
worthyApps.append(app);
}
for (const LtsPluginManager::LtsPluginManager* app : std::as_const(worthyApps))
{
const QVector<LtsSession::LtsSession*> sessions = app->sessions();
for (const LtsSession::LtsSession* session : sessions)
{
if (session->widgetExists(menuGroup, identifier, area))
return true;
}
}
LtsMultiMonitor::LtsMultiMonitor* mm = LtsMultiMonitor::LtsMultiMonitor::getInstance();
LtsMultiMonitor::LtsScreenBase* mainScreen = mm->mainScreen();
for (const LtsPluginManager::LtsPluginManager* app : std::as_const(worthyApps))
{
const QVector<LtsSession::LtsSession*> sessions = app->sessions();
for (LtsSession::LtsSession* session : sessions)
{
if (session != thisSession && mm->widgetsScreen(session->baseLayoutWidget()) == mainScreen &&
requiredPartitionTypes.contains(session->partitionType()))
return true;
}
}
return false;
}
} // namespace LtsAppManager
@@ -1,136 +0,0 @@
#pragma once
#include <Lts/Common/ProjectConfig/project_defines.h>
#include <LtsAppDescriptor/lts_app_descriptor.h>
#include <LtsSession/lts_session.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsWidgets/lts_dialog.h>
#include <QtCore/QObject>
#include <QtCore/QVector>
#include <memory>
#ifndef LTSAPPMANAGER_EXPORT
#ifdef BUILD_STATIC_LIBRARIES
#define LTSAPPMANAGER_EXPORT
#else
#if defined(MAKE_LTSAPPMANAGER_LIB)
#define LTSAPPMANAGER_EXPORT Q_DECL_EXPORT
#else
#define LTSAPPMANAGER_EXPORT Q_DECL_IMPORT
#endif
#endif
#endif
namespace LtsPluginManager
{
class LtsPluginManager;
}
namespace LtsLogin
{
class LtsLogin;
}
namespace LtsSession
{
class LtsSession;
struct DdsConnectionParams;
} // namespace LtsSession
namespace DeveloperTools
{
class DevDialog;
}
class QTcpSocket;
namespace LtsAppManager
{
class LTSAPPMANAGER_EXPORT LtsAppManager : public QObject
{
Q_OBJECT
public:
static LtsAppManager* getInstance();
void showLogin();
LtsPluginManager::LtsPluginManager* createApp(LtsAppDescriptor::App appId,
const LtsSession::DdsConnectionParams& connParams,
const QString& workspaceId = QString(), bool hidden = false,
bool force = false);
bool startAppOrLetUserChoose(LtsAppDescriptor::App appId, const LtsSession::DdsConnectionParams& connParams,
bool force = false);
void setLoginPluginIid(const QString& loginPluginIid)
{
m_loginPluginIid = loginPluginIid;
}
void setMainApp(LtsAppDescriptor::App appId = LtsAppDescriptor::App::Login);
QVector<LtsPluginManager::LtsPluginManager*> getAppList() const
{
return m_appList;
}
LtsAppDescriptor::AppDescriptionVector getAvailableApps() const
{
return m_availableApps;
}
void setAvailableApps(const LtsAppDescriptor::AppDescriptionVector& availableApps);
bool hasRightForApp(const LtsAppDescriptor::AppDescription& appDescription) const;
LtsAppDescriptor::AppDescription getApp(LtsAppDescriptor::App appId) const;
bool hasApp(LtsAppDescriptor::App appId) const;
int getNumberOfSituationMapInstances() const;
void debugSendMessage(const QString& msg);
bool otherSessionsResponsibility(LtsSession::LtsSession* thisSession,
const QVector<LtsAppDescriptor::App>& requiredAppTypes,
const QVector<IDL::PartitionType>& requiredPartitionTypes,
const QString& menuGroup, const QString& identifier, LtsWidgets::LayoutArea area);
private:
friend struct LtsAppManagerHandler;
friend class DeveloperTools::DevDialog;
LtsAppManager();
~LtsAppManager();
bool startAppOrLetUserChoose(const LtsAppDescriptor::AppDescription& appDescription,
const LtsSession::DdsConnectionParams& connParams, bool force = false);
LtsPluginManager::LtsPluginManager* createApp(const LtsAppDescriptor::AppDescription& appDescription,
const LtsSession::DdsConnectionParams& connParams,
const QString& workspaceId = QString(), bool hidden = false,
bool force = false);
void saveState();
LtsAppDescriptor::AppDescriptionVector m_availableApps;
QVector<LtsPluginManager::LtsPluginManager*> m_appList;
LtsLogin::LtsLogin* m_login;
LtsAppDescriptor::AppDescription m_app;
QString m_loginPluginIid;
public slots:
void slotLogout();
private slots:
void slotQuit();
void slotSoftQuit();
void slotShowLogin();
void slotAppDestroyed();
void slotStartDebugServer();
void hideAllOtherBLWs(QObject* _caller);
void taskSwitch(QObject* _caller);
signals:
void signalLoginDone();
void signalLogoffDone();
void signalShowLogin();
void signalInitializationFinished();
void signalCommand(QTcpSocket* client, const QStringList argList);
};
} // namespace LtsAppManager
@@ -1,12 +0,0 @@
find_package(Botan2)
if (BOTAN_FOUND AND CMAKE_VERSION VERSION_GREATER 3.15.9) # botan doesn't use cmake, make it a cmake target | imported targets don't work correctly with 3.10
add_library(LtsBotan STATIC IMPORTED GLOBAL)
set_target_properties(LtsBotan PROPERTIES IMPORTED_LOCATION ${BOTAN_LIBRARIES})
set_target_properties(LtsBotan PROPERTIES INCLUDE_DIRECTORIES ${BOTAN_INCLUDE_DIRS})
target_compile_definitions(LtsBotan INTERFACE BOTAN_FOUND)
target_include_directories(LtsBotan SYSTEM INTERFACE ${BOTAN_INCLUDE_DIRS})
set(LTS_BOTAN LtsBotan)
message(STATUS "BOTAN_INCLUDE_DIRS = ${BOTAN_INCLUDE_DIRS}, BOTAN_LIBRARIES = ${BOTAN_LIBRARIES}, LTS_BOTAN = ${LTS_BOTAN}")
endif()
@@ -1,30 +0,0 @@
#
# $Id$
# $HeadURL$
# $Author$
#
NGL_ADD_LIBRARY2(WS SV HLA LtsCoordinate SHARED
lts_coordinate.h
lts_coordinate.cpp
lts_coord_vector.h
lts_coord_vector.cpp
)
NGL_TARGET_INCLUDE_DIRECTORIES(LtsCoordinate
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_INCLUDE_DIRECTORIES(LtsCoordinate
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../LtsCoordTrans
)
NGL_TARGET_LINK_LIBRARIES(LtsCoordinate
PRIVATE
LtsCoordTrans
PUBLIC
$<IF:$<TARGET_EXISTS:ddscore>,ddscore,>
LtsUtilsCoreHeaderOnly
Qt6::Core
)
ADD_SUBDIRECTORY(UnitTest_Coordinate)
@@ -1,38 +0,0 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
# Make ${Qt6_Gui} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui)
set(Qt6_Gui ${Qt6_Gui})
else()
set(Qt6_Gui "")
endif()
SET(UnitTestSources
lts_coordinate_unit_test.cpp
lts_coordinate_unit_test.h
)
IF(BUILD_TESTING AND TARGET LtsElevation)
ADD_EXECUTABLE(commonUnitTestCoordinate ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(commonUnitTestCoordinate
NGLLogger
nglcoreapplication
LtsCoordinate
LtsElevation
LtsCoordTrans
situationMapCommon
situationMapElevationMap
Qt6::Core
${Qt6_Gui}
Qt6::Test)
ADD_TEST(NAME commonUnitTestCoordinate COMMAND $<TARGET_FILE:commonUnitTestCoordinate>)
ENDIF()
@@ -1,868 +0,0 @@
#include "lts_coordinate_unit_test.h"
#include <ElevationMap/elevation_map_manager.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsCoordinate/lts_coord_vector.h>
#include <LtsCoordinate/lts_coordinate.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtTest/QTest>
#include <nglapplication/nglcoreapplication.h>
Q_DECLARE_METATYPE(LtsUtils::QPoint3D);
#include <iomanip>
#include <iostream>
static const int s_id{4711};
static LtsElevation::LtsElevationManager* s_eleMgr{nullptr};
class MyLtsElevationManagerBootstrap : public LtsElevation::LtsElevationManager
{
using BaseClass = LtsElevation::LtsElevationManager;
public:
static LtsElevationManager* createInstanceUnitTest(int id, const QStringList& filePaths_rough_to_fine)
{
return BaseClass::createInstanceUnitTest(id, filePaths_rough_to_fine);
}
};
UnitTestCoordinate::UnitTestCoordinate()
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTestCoordinate"))
{
}
void UnitTestCoordinate::initTestCase()
{
QString filePath(QStringLiteral("/usr/share/arcgis/resources/elevation/Altmark_dt1.tif"));
s_eleMgr = MyLtsElevationManagerBootstrap::createInstanceUnitTest(s_id, {filePath});
const bool bIsValid = s_eleMgr->isValid();
std::cerr << "myBootstrapMgr->isValid() 1 = " << bIsValid << std::endl;
{
s_eleMgr->waitForInitialization();
const bool bIsValid = s_eleMgr->isValid();
std::cerr << "myBootstrapMgr->isValid() 2 = " << bIsValid << std::endl;
}
QVERIFY2(s_eleMgr, "static LtsElevationManager does not exist");
using namespace std::placeholders;
LtsCoordTrans::LtsCoordinate::elevationCbFunc elevationCB =
std::bind(&LtsElevation::LtsElevationManager::elevation, s_eleMgr, _1, _2, _3, _4);
LtsCoordTrans::LtsCoordinate::setElevationCallback(elevationCB);
QVERIFY2(LtsCoordTrans::LtsCoordinate::hasElevationCallback(), "setElevationCallback failed");
}
void UnitTestCoordinate::init()
{
QVERIFY2(s_eleMgr, "static LtsElevationManager does not exist");
QVERIFY2(LtsCoordTrans::LtsCoordinate::hasElevationCallback(), "setElevationCallback failed");
}
void UnitTestCoordinate::test_NaN()
{
bool equal = qFuzzyCompare(std::nan(""), std::nan(""));
NOTICE_LOG(m_logger) << " qFuzzyCompare(NaN, NaN) = " << equal;
QVERIFY2(equal == false, "qFuzzyCompare(NaN, NaN) failed");
}
void UnitTestCoordinate::test_coordinate_simple1()
{
LtsCoordTrans::LtsCoordinate ltsCoord;
QVERIFY2(ltsCoord.isValid() == false, "isValid failed");
const Core::GeoPosition& geoPos = ltsCoord.geoPosition();
NOTICE_LOG(m_logger) << " geoPosition = " << geoPos;
QVERIFY2(qFuzzyIsNull(geoPos.lon()), "geoPosition failed");
QVERIFY2(qFuzzyIsNull(geoPos.lat()), "geoPosition failed");
QVERIFY2(std::isnan(geoPos.z()), "geoPosition failed");
const QPointF& utm = ltsCoord.toUtmPointF();
const QPointF& utm2 = ltsCoord.toUtm<QPointF>();
const LtsCoordTrans::UtmCoordinate3D& utm3D = ltsCoord.toUtmCoordinate3D();
NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm;
NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm2;
NOTICE_LOG(m_logger) << " UtmCoordinate3D = " << utm3D;
QVERIFY2(qFuzzyCompare(utm3D.x, 166021.44308053952), "data no equal");
QVERIFY2(qFuzzyCompare(utm3D.y, 0.0), "data no equal");
QVERIFY2(utm3D.zone == 31, "data no equal");
QVERIFY2(utm == utm2, "data no equal");
const Core::GeoPosition& geoPos2 = ltsCoord.geoPosition();
NOTICE_LOG(m_logger) << " geoPosition2 = " << geoPos2;
QVERIFY2(qFuzzyIsNull(geoPos2.lon()), "geoPosition2 failed");
QVERIFY2(qFuzzyIsNull(geoPos2.lat()), "geoPosition2 failed");
QVERIFY2(std::isnan(geoPos2.z()), "geoPosition2 failed");
}
void UnitTestCoordinate::test_coordinate_simple2()
{
Core::GeoPosition defaultGeoPos;
LtsCoordTrans::LtsCoordinate ltsCoord(defaultGeoPos);
QVERIFY2(ltsCoord.isValid() == false, "isValid failed");
const Core::GeoPosition& geoPos = ltsCoord.geoPosition();
NOTICE_LOG(m_logger) << " geoPosition = " << geoPos;
QVERIFY2(qFuzzyIsNull(geoPos.lon()), "geoPosition failed");
QVERIFY2(qFuzzyIsNull(geoPos.lat()), "geoPosition failed");
QVERIFY2(qFuzzyIsNull(geoPos.z()), "geoPosition failed");
const QPointF& utm = ltsCoord.toUtm<QPointF>();
NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm;
const Core::GeoPosition& geoPos2 = ltsCoord.geoPosition();
NOTICE_LOG(m_logger) << " geoPosition2 = " << geoPos2;
QVERIFY2(qFuzzyIsNull(geoPos2.lon()), "geoPosition2 failed");
QVERIFY2(qFuzzyIsNull(geoPos2.lat()), "geoPosition2 failed");
QVERIFY2(qFuzzyIsNull(geoPos2.z()), "geoPosition2 failed");
}
void UnitTestCoordinate::test_coordinate_simple3()
{
static const Core::GeoPosition s_invalidPos(std::nan(""), std::nan(""), std::nan(""));
LtsCoordTrans::LtsCoordinate ltsCoord(s_invalidPos);
QVERIFY2(ltsCoord.isValid() == false, "isValid failed");
const Core::GeoPosition& geoPos = ltsCoord.geoPosition();
NOTICE_LOG(m_logger) << " geoPosition = " << geoPos;
QVERIFY2(std::isnan(geoPos.lon()), "geoPosition failed");
QVERIFY2(std::isnan(geoPos.lat()), "geoPosition failed");
QVERIFY2(std::isnan(geoPos.z()), "geoPosition failed");
const QPointF& utm = ltsCoord.toUtm<QPointF>();
NOTICE_LOG(m_logger) << " utmCoordinateAsPointF = " << utm;
QVERIFY2(utm.isNull(), "utm failed");
}
void UnitTestCoordinate::test_coordinate_simple4()
{
LtsCoordTrans::LtsCoordinate::resetConversionCounter();
QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 0, "getConversionCounter failed");
const LtsCoordTrans::UtmCoordinate& utmIn =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
const double zValue = 2.3;
const LtsCoordTrans::LtsCoordinate ltsCoord(utmIn.geographic(), zValue);
const LtsCoordTrans::UtmCoordinate& utmCoord_FirstTry = ltsCoord.utmCoordinate();
Q_UNUSED(utmCoord_FirstTry);
QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed");
const LtsCoordTrans::UtmCoordinate& utmCoord_SecondTry = ltsCoord.utmCoordinate();
Q_UNUSED(utmCoord_SecondTry);
QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed");
}
void UnitTestCoordinate::test_coordinate_fromUtm()
{
const LtsCoordTrans::UtmCoordinate& utmIn =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
const double zValue = 2.3;
NOTICE_LOG(m_logger) << " utmIn = " << utmIn;
LtsCoordTrans::LtsCoordinate ltsCoord(utmIn.geographic(), zValue);
const LtsCoordTrans::UtmCoordinate& utmCoord = ltsCoord.utmCoordinate();
NOTICE_LOG(m_logger) << " utmCoord = " << utmCoord;
NOTICE_LOG(m_logger) << " utmCoord = " << utmCoord.mgrs();
QVERIFY2(utmCoord.mgrs() == QStringLiteral("32U PD 70000 10000"), "utmCoordinate failed");
const Core::GeoPosition& geoPos = ltsCoord.geoPosition();
NOTICE_LOG(m_logger) << " geoPosition = " << geoPos;
QVERIFY2(qFuzzyCompare(geoPos.lon(), 0.200706392814992), "geoPosition failed");
QVERIFY2(qFuzzyCompare(geoPos.lat(), 0.914792952478694), "geoPosition failed");
QVERIFY2(qFuzzyCompare(geoPos.z(), zValue), "geoPosition failed");
const QPointF& utmP2D = ltsCoord.toUtm<QPointF>();
NOTICE_LOG(m_logger) << " utmP2D = " << utmP2D;
QVERIFY2(qFuzzyCompare(utmP2D.x(), 670000.5), "utm1 failed");
QVERIFY2(qFuzzyCompare(utmP2D.y(), 5810000.5), "utm2 failed");
const LtsUtils::QPoint3D& utmP3D = ltsCoord.toUtm<LtsUtils::QPoint3D>();
NOTICE_LOG(m_logger) << " utmP3D = " << utmP3D;
QVERIFY2(qFuzzyCompare(utmP3D.x(), 670000.5), "utm1 failed");
QVERIFY2(qFuzzyCompare(utmP3D.y(), 5810000.5), "utm2 failed");
QVERIFY2(qFuzzyCompare(utmP3D.z(), zValue), "utm3 failed");
const LtsCoordTrans::UtmCoordinate3D& utmCoord3D = ltsCoord.toUtmCoordinate3D();
NOTICE_LOG(m_logger) << " UtmCoordinate3D = " << utmCoord3D;
QVERIFY2(qFuzzyCompare(utmCoord3D.x, 670000.5), "utmCoord3D failed");
QVERIFY2(qFuzzyCompare(utmCoord3D.y, 5810000.5), "utmCoord3D failed");
QVERIFY2(qFuzzyCompare(utmCoord3D.z(), zValue), "utmCoord3D failed");
const LtsCoordTrans::UtmCoordinate& utmCoord2D = utmCoord3D;
NOTICE_LOG(m_logger) << " utmCoord2D = " << utmCoord2D;
QVERIFY2(qFuzzyCompare(utmCoord2D.x, 670000.5), "utmCoord2D failed");
QVERIFY2(qFuzzyCompare(utmCoord2D.y, 5810000.5), "utmCoord2D failed");
const LtsCoordTrans::UtmCoordinate& utm = ltsCoord.utmCoordinate();
QVERIFY2(utm.mgrs() == QStringLiteral("32U PD 70000 10000"), "utmCoordinate failed");
}
void UnitTestCoordinate::test_coordinate_fromUtm2()
{
const LtsCoordTrans::UtmCoordinate& utmInZone32 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LA 10000 00000"));
const double zValue = 2.3;
NOTICE_LOG(m_logger) << " utmInZone32 = " << utmInZone32;
const QPointF& utmInDefQPointF = utmInZone32.geographic();
const int zone31 = 31;
const LtsCoordTrans::UtmCoordinate& utmInZone31 =
LtsCoordTrans::UtmCoordinate::fromGeographic(utmInDefQPointF, zone31);
NOTICE_LOG(m_logger) << " utmInZone31 = " << utmInZone31;
NOTICE_LOG(m_logger) << " utmInZone31.geographic() = " << utmInZone31.geographic();
NOTICE_LOG(m_logger) << " utmInZone32.geographic() = " << utmInZone32.geographic();
QVERIFY2(utmInZone31.geographic() == utmInZone32.geographic(), "geographic failed");
{
LtsCoordTrans::LtsCoordinate ltsFromUtm31(utmInZone31.geographic(), zValue);
LtsCoordTrans::LtsCoordinate ltsFromUtm32(utmInZone32.geographic(), zValue);
double dist = ltsFromUtm31.distanceUtm3D(ltsFromUtm32);
NOTICE_LOG(m_logger) << " dist1 = " << dist;
QVERIFY2(qFuzzyCompare(dist, 0), "distance3D");
}
{
LtsCoordTrans::LtsCoordinate ltsFromUtm31(utmInZone31.geographic(), zValue);
LtsCoordTrans::LtsCoordinate ltsFromUtm32(utmInZone32.geographic(1, 0), zValue);
double dist = ltsFromUtm31.distanceUtm3D(ltsFromUtm32);
NOTICE_LOG(m_logger) << " dist2 = " << dist;
QVERIFY2(fabs(dist - 1) < 0.00001, "distance3D");
}
{
LtsCoordTrans::LtsCoordinate ltsFromUtm31 =
LtsCoordTrans::LtsCoordinate::fromUtmCoordinate(utmInZone31, zValue);
LtsCoordTrans::LtsCoordinate ltsFromUtm32 =
LtsCoordTrans::LtsCoordinate::fromUtmCoordinate(utmInZone32, zValue);
double dist = ltsFromUtm31.distanceUtm3D(ltsFromUtm32);
NOTICE_LOG(m_logger) << " dist3 = " << dist;
QVERIFY2(qFuzzyCompare(dist, 0), "distance3D");
}
{
LtsCoordTrans::LtsCoordinate ltsFromUtm32(utmInZone32.geographic(), zValue);
const QPointF& utmP2D_def = ltsFromUtm32.toUtm<QPointF>();
const QPointF& utmP2D_31 = ltsFromUtm32.toUtm<QPointF>(31);
NOTICE_LOG(m_logger) << " utmP2D_def = " << utmP2D_def;
NOTICE_LOG(m_logger) << " utmP2D_31 = " << utmP2D_31;
LtsCoordTrans::LtsCoordinate lts1 = LtsCoordTrans::LtsCoordinate::fromUtm(utmP2D_def, 32, 0.0);
LtsCoordTrans::LtsCoordinate lts2 = LtsCoordTrans::LtsCoordinate::fromUtm(utmP2D_31, 31, 0.0);
NOTICE_LOG(m_logger) << " lts1 = " << lts1;
NOTICE_LOG(m_logger) << " lts2 = " << lts2;
double dist = lts1.distanceUtm3D(lts2);
NOTICE_LOG(m_logger) << " dist4 = " << dist;
QVERIFY2(qFuzzyCompare(dist, 0), "distance3D");
}
}
void UnitTestCoordinate::test_coordinate_elevationAt()
{
const double zValue = 1.23;
const LtsCoordTrans::UtmCoordinate& utmIn =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
NOTICE_LOG(m_logger) << " utmIn = " << utmIn;
LtsCoordTrans::LtsCoordinate ltsFromUtm(utmIn.geographic(), zValue);
NOTICE_LOG(m_logger) << " ltsFromUtm1 = " << ltsFromUtm;
QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), zValue), "geoPosition failed");
const double ele = ltsFromUtm.elevationAt();
QVERIFY2(qFuzzyCompare(ele, 75), "elevationAt failed");
NOTICE_LOG(m_logger) << " ltsFromUtm2 = " << ltsFromUtm;
QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), zValue), "geoPosition failed");
ltsFromUtm.setZValueAccordingElevation();
NOTICE_LOG(m_logger) << " ltsFromUtm3 = " << ltsFromUtm;
QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), 75), "setZValueAccordingElevation failed");
const LtsUtils::QPoint3D& utmP3D = ltsFromUtm.toUtm<LtsUtils::QPoint3D>();
NOTICE_LOG(m_logger) << " utmP3D_a = " << utmP3D;
QVERIFY2(qFuzzyCompare(utmP3D.z(), 75), "utmCoordinateAsPoint3D failed");
{
const double ele3 = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm);
NOTICE_LOG(m_logger) << " ele3 = " << ele3;
QVERIFY2(qFuzzyCompare(ele, ele3), "LtsCoordinate::elevationAt failed");
}
LtsCoordTrans::UtmCoordinate utmMoved = ltsFromUtm.utmCoordinate();
utmMoved.x += 11;
utmMoved.y += 22;
const int zValueNew = 123;
ltsFromUtm = LtsCoordTrans::LtsCoordinate(utmMoved.geographic(), zValueNew);
NOTICE_LOG(m_logger) << " ltsFromUtm4 = " << ltsFromUtm;
const double ele2 = ltsFromUtm.elevationAt();
NOTICE_LOG(m_logger) << " ltsFromUtm5 = " << ltsFromUtm;
QVERIFY2(ele2 != 75, "setZValueAccordingElevation failed");
}
void UnitTestCoordinate::test_coordinate_elevationAt_isCovered()
{
const LtsCoordTrans::UtmCoordinate& utmIn1 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
const LtsCoordTrans::UtmCoordinate& utmIn2 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U MD 10000 10000"));
LtsCoordTrans::LtsCoordinate ltsFromUtm1(utmIn1.geographic(), 11);
LtsCoordTrans::LtsCoordinate ltsFromUtm2(utmIn2.geographic(), 11);
{
bool bIsCovered = false;
double ele = ltsFromUtm1.elevationAt(-123, &bIsCovered);
Q_UNUSED(ele);
QVERIFY2(bIsCovered == true, "elevationAt1a failed");
bIsCovered = false;
ele = ltsFromUtm1.elevationAt(-123, &bIsCovered);
Q_UNUSED(ele);
QVERIFY2(bIsCovered == true, "elevationAt1b failed");
bIsCovered = false;
ele = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm1, -123, &bIsCovered);
Q_UNUSED(ele);
QVERIFY2(bIsCovered == true, "elevationAt2a failed");
bIsCovered = false;
ele = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm1, -123, &bIsCovered);
Q_UNUSED(ele);
QVERIFY2(bIsCovered == true, "elevationAt2b failed");
}
{
bool bIsCovered = true;
double ele = ltsFromUtm2.elevationAt(-123, &bIsCovered);
Q_UNUSED(ele);
QVERIFY2(bIsCovered == false, "elevationAt2 failed");
bIsCovered = true;
ele = LtsCoordTrans::LtsCoordinate::elevationAt(ltsFromUtm2, -123, &bIsCovered);
Q_UNUSED(ele);
QVERIFY2(bIsCovered == false, "elevationAt1 failed");
}
}
void UnitTestCoordinate::test_coordinate_elevation_data()
{
QTest::addColumn<QString>("mgrs");
QTest::addColumn<double>("elevationIn");
QTest::addColumn<double>("elevationRef");
QTest::newRow("elevation 10 ") << QStringLiteral("32 U PD 70000 10000") << std::nan("") << 75.0;
QTest::newRow("elevation 11 ") << QStringLiteral("32 U PD 70000 10000") << 0.0 << 75.0;
QTest::newRow("elevation 20 ") << QStringLiteral("32 U PD 72000 13000") << std::nan("") << 80.0;
QTest::newRow("elevation 21 ") << QStringLiteral("32 U PD 72000 13000") << 0.340 << 80.0;
}
void UnitTestCoordinate::test_coordinate_elevation()
{
QFETCH(QString, mgrs);
QFETCH(double, elevationIn);
QFETCH(double, elevationRef);
const LtsCoordTrans::UtmCoordinate& utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs);
NOTICE_LOG(m_logger) << " utmIn = " << utmIn;
LtsCoordTrans::LtsCoordinate ltsFromUtm(utmIn.geographic(), elevationIn);
NOTICE_LOG(m_logger) << " ltsFromUtm = " << ltsFromUtm;
NOTICE_LOG(m_logger) << " geoPosition = " << ltsFromUtm.geoPosition();
ltsFromUtm.replaceZValueByElevationIfValue(elevationIn);
NOTICE_LOG(m_logger) << " geoPosition = " << ltsFromUtm.geoPosition();
QVERIFY2(qFuzzyCompare(ltsFromUtm.geoPosition().z(), elevationRef), "elevation failed");
const LtsUtils::QPoint3D& utmP3D = ltsFromUtm.toUtm<LtsUtils::QPoint3D>();
NOTICE_LOG(m_logger) << " utmP3D = " << utmP3D;
QVERIFY2(qFuzzyCompare(utmP3D.z(), elevationRef), "utmCoordinateAsPoint3D failed");
}
void UnitTestCoordinate::test_coordinate_mgrs()
{
const LtsCoordTrans::UtmCoordinate& utmIn =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
NOTICE_LOG(m_logger) << " utmIn = " << utmIn.mgrs();
LtsCoordTrans::LtsCoordinate ltsFromUtm(utmIn.geographic());
{
const QString asMgrs = ltsFromUtm.mgrs();
NOTICE_LOG(m_logger) << " asMgrs = " << asMgrs;
QVERIFY2(asMgrs.compare(QStringLiteral("32U PD 70000 10000")) == 0, "UtmCoordinate::mgrs failed");
}
{
const QString asMgrs = ltsFromUtm.mgrsWithZ();
NOTICE_LOG(m_logger) << " asMgrs = " << asMgrs;
QVERIFY2(asMgrs.compare(QStringLiteral("32U PD 70000 10000 z=75.00")) == 0, "UtmCoordinate::mgrs failed");
}
{
const LtsCoordTrans::LtsCoordinate coord(0.200706392814992, 0.914792952478694, 123);
const QString str1 = coord.toString();
NOTICE_LOG(m_logger) << " str1 cheap = " << str1;
QVERIFY2(str1.compare(QStringLiteral("LtsCoordinate(m_geo=[[lon: 0.200706392814992, lat: 0.914792952478694]z: "
"123], m_utm2D.x 0, m_utm2D.y 0, m_utm2D.zone -1, elevation nan)")) == 0,
"toString failed");
const bool fullAndExpensive = true;
const QString str2 = coord.toString(fullAndExpensive);
NOTICE_LOG(m_logger) << " str2 expensive = " << str2;
QVERIFY2(str2.compare(QStringLiteral("LtsCoordinate(m_geo=[[lon: 0.200706392814992, lat: 0.914792952478694]z: "
"123], m_utm2D.x 670000.499999998, m_utm2D.y 5810000.5, m_utm2D.zone 32, "
"m_utm2D.mgrs '32U PD 70000 10000', elevation 75.000)")) == 0,
"toString failed");
}
}
void UnitTestCoordinate::test_coordinate_distance()
{
const LtsCoordTrans::UtmCoordinate& utmIn1 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
const LtsCoordTrans::UtmCoordinate& utmIn2 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70010 10100"));
NOTICE_LOG(m_logger) << " XXutmIn1 = " << utmIn1;
NOTICE_LOG(m_logger) << " XXutmIn2 = " << utmIn2;
QVERIFY2(qFuzzyCompare((utmIn2.x - utmIn1.x), 10), "coord failed");
QVERIFY2(qFuzzyCompare((utmIn2.y - utmIn1.y), 100), "coord failed");
const LtsCoordTrans::LtsCoordinate lts1(utmIn1.geographic(), 11);
const LtsCoordTrans::LtsCoordinate lts2(utmIn2.geographic(), 111);
{
double dist2D = lts1.distanceUtm2D(lts2);
NOTICE_LOG(m_logger) << " dist2D = " << dist2D;
QVERIFY2(std::fabs(dist2D - 100.499) < 0.01, "distanceUtm2D failed");
}
{
double dist3D = lts1.distanceUtm3D(lts2);
NOTICE_LOG(m_logger) << " dist3D = " << dist3D;
QVERIFY2(std::fabs(dist3D - 141.774) < 0.01, "distanceUtm3D failed");
}
}
void UnitTestCoordinate::test_coordinate_distanceDiffZones()
{
const LtsCoordTrans::UtmCoordinate& utmIn1 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("31 U FR 99999 00000"));
const LtsCoordTrans::UtmCoordinate& utmIn2 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LA 00000 00000"));
NOTICE_LOG(m_logger) << " XXutmIn1 = " << utmIn1;
NOTICE_LOG(m_logger) << " XXutmIn2 = " << utmIn2;
LtsCoordTrans::LtsCoordinate lts1(utmIn1.geographic(), 0);
LtsCoordTrans::LtsCoordinate lts2(utmIn2.geographic(), 0);
NOTICE_LOG(m_logger) << " lts1 = " << lts1;
NOTICE_LOG(m_logger) << " lts2 = " << lts2;
double dist = lts1.distanceUtm3D(lts2);
NOTICE_LOG(m_logger) << " dist = " << dist;
QVERIFY2(std::fabs(dist - 33382.7) < 0.1, "distance3D failed");
}
void UnitTestCoordinate::test_squareInDifferentZone()
{
const LtsCoordTrans::UtmCoordinate& utm_bl =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
const LtsCoordTrans::UtmCoordinate& utm_br =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 11000"));
const LtsCoordTrans::UtmCoordinate& utm_tl =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 10000"));
const LtsCoordTrans::UtmCoordinate& utm_tr =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 11000"));
const LtsCoordTrans::LtsCoordinate lts_bl(utm_bl.geographic(), 0);
const LtsCoordTrans::LtsCoordinate lts_br(utm_br.geographic(), 0);
const LtsCoordTrans::LtsCoordinate lts_tl(utm_tl.geographic(), 0);
const LtsCoordTrans::LtsCoordinate lts_tr(utm_tr.geographic(), 0);
{
QVERIFY2(qFuzzyCompare(lts_bl.distanceUtm2D(lts_br), 1000.0), "distance3D 1 failed");
QVERIFY2(qFuzzyCompare(lts_tl.distanceUtm2D(lts_tr), 1000.0), "distance3D 2 failed");
QVERIFY2(qFuzzyCompare(lts_br.distanceUtm2D(lts_tr), 1000.0), "distance3D 3 failed");
QVERIFY2(qFuzzyCompare(lts_bl.distanceUtm2D(lts_tl), 1000.0), "distance3D 4 failed");
QVERIFY2(qFuzzyCompare(lts_bl.distanceUtm2D(lts_tr), 1414.213562373177), "distance3D 5 failed");
QVERIFY2(qFuzzyCompare(lts_br.distanceUtm2D(lts_tl), 1414.213562373177), "distance3D 6 failed");
}
{
const LtsCoordTrans::UtmCoordinate& utm31_bl = LtsCoordTrans::UtmCoordinate(31, utm_bl.x, utm_bl.y);
const LtsCoordTrans::UtmCoordinate& utm31_br = LtsCoordTrans::UtmCoordinate(31, utm_br.x, utm_br.y);
const LtsCoordTrans::UtmCoordinate& utm31_tl = LtsCoordTrans::UtmCoordinate(31, utm_tl.x, utm_tl.y);
const LtsCoordTrans::UtmCoordinate& utm31_tr = LtsCoordTrans::UtmCoordinate(31, utm_tr.x, utm_tr.y);
QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_bl, utm31_br), 1000.0),
"LtsCoordTrans::distance 1 failed");
QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_tl, utm31_tr), 1000.0),
"LtsCoordTrans::distance 2 failed");
QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_br, utm31_tr), 1000.0),
"LtsCoordTrans::distance 3 failed");
QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_bl, utm31_tl), 1000.0),
"LtsCoordTrans::distance 4 failed");
QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_bl, utm31_tr), 1414.213562373095),
"LtsCoordTrans::distance 5 failed");
QVERIFY2(qFuzzyCompare(LtsCoordTrans::distance(utm31_br, utm31_tl), 1414.213562373095),
"LtsCoordTrans::distance 6 failed");
}
}
void UnitTestCoordinate::test_coordinate_angle()
{
const LtsCoordTrans::UtmCoordinate& utm_ul =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
const LtsCoordTrans::LtsCoordinate lts_ul(utm_ul.geographic(), 0);
{
const LtsCoordTrans::UtmCoordinate& utm_2 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 11000"));
const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0);
double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2);
QVERIFY2(qFuzzyCompare(angle, 0.0), "angleUtm2D 1 failed");
}
{
const LtsCoordTrans::UtmCoordinate& utm_2 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 10000"));
const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0);
double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2);
QVERIFY2(qFuzzyCompare(angle, 90.0), "angleUtm2D 1 failed");
}
{
const LtsCoordTrans::UtmCoordinate& utm_2 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 09000"));
const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0);
double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2);
QVERIFY2(qFuzzyCompare(angle, 180.0), "angleUtm2D 1 failed");
}
{
const LtsCoordTrans::UtmCoordinate& utm_2 =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 69000 10000"));
const LtsCoordTrans::LtsCoordinate lts_2(utm_2.geographic(), 0);
double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_2);
QVERIFY2(qFuzzyCompare(angle, 270.0), "angleUtm2D 1 failed");
}
{
const LtsCoordTrans::UtmCoordinate& utm_or =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 71000 12000"));
const LtsCoordTrans::LtsCoordinate lts_or(utm_or.geographic(), 0);
double angle = LtsCoordTrans::LtsCoordinate::angleDegreeUtm2D(lts_ul, lts_or);
QVERIFY2(qFuzzyCompare(angle, 26.565051177056659526), "angleUtm2D 1 failed");
QVERIFY2((angle - 26.5651 < 0.001), "angleUtm2D 1 failed");
}
}
void UnitTestCoordinate::test_coordinate_cont()
{
std::vector<Core::GeoPosition> stdVec;
stdVec.push_back(Core::GeoPosition(0.1, 0.11, 1));
stdVec.push_back(Core::GeoPosition(0.2, 0.22, 2));
stdVec.push_back(Core::GeoPosition(0.3, 0.33, 3));
const QVector<LtsCoordTrans::LtsCoordinate>& newCont =
LtsCoordTrans::convert<QVector<LtsCoordTrans::LtsCoordinate>>(stdVec);
NOTICE_LOG(m_logger) << " newCont.size() = " << newCont.size();
QVERIFY2(stdVec.size() == static_cast<size_t>(newCont.size()), "convert failed");
}
using namespace LtsCoordTrans;
class ParticpantPositionStore
{
public:
ParticpantPositionStore() = default;
~ParticpantPositionStore() = default;
void setGeoPosition(const Core::GeoPosition& newGeoPos)
{
m_ltsCoord = LtsCoordinate(newGeoPos);
m_ltsCoord_NoImplicitCopy.updateGeoPosition(newGeoPos);
}
LtsCoordinate getLtsCoord_copy()
{
return m_ltsCoord;
}
const LtsCoordinateNoImplicitCopy& getLtsCoord_noCopy()
{
return m_ltsCoord_NoImplicitCopy;
}
LtsCoordinate m_ltsCoord;
LtsCoordinateNoImplicitCopy m_ltsCoord_NoImplicitCopy;
};
void UnitTestCoordinate::test_coordinate_avoid_copy()
{
LtsCoordTrans::LtsCoordinate::resetConversionCounter();
QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 0, "getConversionCounter failed");
ParticpantPositionStore store;
const UtmCoordinate& utmIn = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U PD 70000 10000"));
const LtsCoordinate ltsIn(utmIn.geographic(), 11);
store.getLtsCoord_noCopy().updateGeoPosition(ltsIn.geoPosition());
const LtsCoordinateNoImplicitCopy& justReference = store.getLtsCoord_noCopy();
NOTICE_LOG(m_logger) << " justReference = " << justReference.mgrs();
const LtsCoordTrans::UtmCoordinate& convertedToUtm = justReference.utmCoordinate();
NOTICE_LOG(m_logger) << " convertedToUtm = " << convertedToUtm;
QVERIFY2(qFuzzyCompare(convertedToUtm.x, 670000.5), "convert failed");
QVERIFY2(qFuzzyCompare(convertedToUtm.y, 5810000.5), "convert failed");
QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed");
const LtsCoordTrans::UtmCoordinate& convertedToUtmSecondTry = justReference.utmCoordinate();
Q_UNUSED(convertedToUtmSecondTry)
QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed");
std::vector<LtsCoordinateNoImplicitCopy> myVec;
myVec.push_back(justReference);
const LtsCoordTrans::LtsCoordinateNoImplicitCopy& retrievedUtmRef = myVec.front();
NOTICE_LOG(m_logger) << " retrievedUtm = " << retrievedUtmRef.mgrs();
QVERIFY2(justReference.mgrs() == retrievedUtmRef.mgrs(), "getConversionCounter failed");
QVERIFY2(LtsCoordTrans::LtsCoordinate::getConversionCounter() == 1, "getConversionCounter failed");
}
template <typename T> void removeItem(T& map, int key)
{
map.erase(key);
}
template <> void removeItem(QMap<int, int>& map, int key)
{
map.remove(key);
}
template <> void removeItem(QHash<int, int>& map, int key)
{
map.remove(key);
}
template <typename T> void test_references(T& map)
{
for (int i = 2; i < 10; ++i)
map[i] = i;
const int& ref2 = map[2];
const int& ref3 = map[3];
const int& ref4 = map[4];
const int& ref5 = map[5];
const int& ref6 = map[6];
const int& ref7 = map[7];
const int& ref8 = map[8];
QVERIFY2(ref2 == 2, "ref failed");
QVERIFY2(ref3 == 3, "ref failed");
QVERIFY2(ref4 == 4, "ref failed");
QVERIFY2(ref5 == 5, "ref failed");
QVERIFY2(ref6 == 6, "ref failed");
QVERIFY2(ref7 == 7, "ref failed");
QVERIFY2(ref8 == 8, "ref failed");
removeItem(map, 1);
removeItem(map, 3);
removeItem(map, 5);
{
QVERIFY2(ref2 == 2, "ref failed");
QVERIFY2(ref4 == 4, "ref failed");
QVERIFY2(ref6 == 6, "ref failed");
QVERIFY2(ref7 == 7, "ref failed");
QVERIFY2(ref8 == 8, "ref failed");
}
map[0] = 0;
map[1] = 1;
const int& ref0 = map[0];
const int& ref1 = map[1];
{
QVERIFY2(ref0 == 0, "ref failed");
QVERIFY2(ref1 == 1, "ref failed");
QVERIFY2(ref2 == 2, "ref failed");
QVERIFY2(ref4 == 4, "ref failed");
QVERIFY2(ref6 == 6, "ref failed");
QVERIFY2(ref7 == 7, "ref failed");
QVERIFY2(ref8 == 8, "ref failed");
}
}
void UnitTestCoordinate::test_container_references()
{
{
std::unordered_map<int, int> std_unordered_map;
test_references(std_unordered_map);
}
{
std::map<int, int> std_map;
test_references(std_map);
}
{
QMap<int, int> Qmap;
test_references(Qmap);
}
{
QHash<int, int> Qhash;
test_references(Qhash);
}
}
void UnitTestCoordinate::test_coordVector()
{
std::vector<Core::GeoPosition> geoVec;
{
QPointF utmPnt = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LB 00001 50001")).geographic();
geoVec.push_back(Core::GeoPosition(utmPnt.x(), utmPnt.y(), 1.1));
utmPnt = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LB 00001 50001")).geographic(1, 0);
geoVec.push_back(Core::GeoPosition(utmPnt.x(), utmPnt.y(), 2.2));
utmPnt = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32 U LB 00001 50001")).geographic(2, 0);
geoVec.push_back(Core::GeoPosition(utmPnt.x(), utmPnt.y(), 3.3));
}
const LtsCoordVector coordVec(geoVec);
QVERIFY2(coordVec.size() == 3, "size failed");
int cnt = LtsCoordTrans::getConversionCounter_UTMUPS_Forward();
{
const std::vector<LtsCoordTrans::UtmCoordinate>& utmVec = coordVec.toStdUtmCoord(32);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed");
QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 3,
"getConversionCounterUTMUPS_Forward failed");
}
{
const std::vector<LtsCoordTrans::UtmCoordinate>& utmVec = coordVec.toStdUtmCoord(32);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed");
QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 3,
"getConversionCounterUTMUPS_Forward failed");
}
{
const std::vector<LtsCoordTrans::UtmCoordinate>& utmVec = coordVec.toStdUtmCoord(31);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6,
"getConversionCounterUTMUPS_Forward failed");
}
{
const std::vector<LtsCoordTrans::UtmCoordinate>& utmVec = coordVec.toStdUtmCoord(31);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6,
"getConversionCounterUTMUPS_Forward failed");
}
{
const QVector<LtsCoordTrans::UtmCoordinate>& utmVec = coordVec.toQVectorUtmCoord(32);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed");
QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6,
"getConversionCounterUTMUPS_Forward failed");
}
{
const QVector<LtsCoordTrans::UtmCoordinate>& utmVec = coordVec.toQVectorUtmCoord(32);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(utmVec.front().mgrs() == QStringLiteral("32U LB 00001 50001"), "mgrs failed");
QVERIFY2(utmVec.back().mgrs() == QStringLiteral("32U LB 00003 50001"), "mgrs failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6,
"getConversionCounterUTMUPS_Forward failed");
}
{
const QVector<QPointF>& utmVec = coordVec.toQVectorQPointF(32);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6,
"getConversionCounterUTMUPS_Forward failed");
}
{
const QVector<LtsCoordTrans::UtmCoordinate>& utmVec = coordVec.toQVectorUtmCoord(32);
QVERIFY2(utmVec.size() == 3, "size failed");
QVERIFY2(LtsCoordTrans::getConversionCounter_UTMUPS_Forward() - cnt == 6,
"getConversionCounterUTMUPS_Forward failed");
}
}
int main(int argc, char* argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTestCoordinate", "log4cxxPositionUnitTest.props");
UnitTestCoordinate tc;
int ret = QTest::qExec(&tc, argc, argv);
if (s_eleMgr)
{
s_eleMgr->cleanupAll();
}
return ret;
}
@@ -1,60 +0,0 @@
#pragma once
#include <ElevationMap/elevation_map_manager.h>
#include <LtsElevation/lts_elevation_manager.h>
#include <NGLLogger/INGLLogger.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtTest/QTest>
using LOGGER_T = NGL::INGLLogger&;
class UnitTestCoordinate : public QObject
{
Q_OBJECT
public:
UnitTestCoordinate();
private Q_SLOTS:
void initTestCase();
void init();
void test_NaN();
void test_coordinate_simple1();
void test_coordinate_simple2();
void test_coordinate_simple3();
void test_coordinate_simple4();
void test_coordinate_fromUtm();
void test_coordinate_fromUtm2();
void test_coordinate_elevationAt();
void test_coordinate_elevationAt_isCovered();
void test_coordinate_elevation_data();
void test_coordinate_elevation();
void test_coordinate_mgrs();
void test_coordinate_distance();
void test_coordinate_distanceDiffZones();
void test_squareInDifferentZone();
void test_coordinate_angle();
void test_coordinate_cont();
void test_coordinate_avoid_copy();
void test_container_references();
void test_coordVector();
private:
NGL::INGLLogger& m_logger;
};
@@ -1,69 +0,0 @@
#include "lts_coord_vector.h"
namespace LtsCoordTrans
{
LtsCoordVector::LtsCoordVector(const std::vector<Core::GeoPosition>& geo_coordinates)
: m_geo_coordinates(geo_coordinates)
{
}
const std::vector<UtmCoordinate>& LtsCoordVector::toStdUtmCoord(int zone) const
{
convertGeoToStdUtm(zone);
return m_utmMapStd[zone];
}
const QVector<UtmCoordinate>& LtsCoordVector::toQVectorUtmCoord(int zone) const
{
convertGeoToQVectorUtm(zone);
return m_utmMapQVector[zone];
}
const QVector<QPointF>& LtsCoordVector::toQVectorQPointF(int zone) const
{
convertGeoToQVectorQPointF(zone);
return m_utmMapQVectorQPointF[zone];
}
void LtsCoordVector::convertGeoToStdUtm(int zone) const
{
const auto it = m_utmMapStd.find(zone);
if (it != m_utmMapStd.end())
return;
convertGeoToUtmInternal(zone, m_utmMapStd[zone]);
}
void LtsCoordVector::convertGeoToQVectorUtm(int zone) const
{
const auto it = m_utmMapQVector.find(zone);
if (it != m_utmMapQVector.end())
return;
convertGeoToStdUtm(zone);
QVector<LtsCoordTrans::UtmCoordinate>& utmVectorRef = m_utmMapQVector[zone];
utmVectorRef.clear();
utmVectorRef.reserve(m_geo_coordinates.size());
for (const auto& utmCoord : m_utmMapStd[zone])
utmVectorRef.push_back(utmCoord);
}
void LtsCoordVector::convertGeoToQVectorQPointF(int zone) const
{
const auto it = m_utmMapQVectorQPointF.find(zone);
if (it != m_utmMapQVectorQPointF.end())
return;
convertGeoToStdUtm(zone);
QVector<QPointF>& QVectorQPointFRef = m_utmMapQVectorQPointF[zone];
QVectorQPointFRef.clear();
QVectorQPointFRef.reserve(m_geo_coordinates.size());
for (const auto& utmCoord : m_utmMapStd[zone])
QVectorQPointFRef.push_back(QPointF(utmCoord.x, utmCoord.y));
}
} // namespace LtsCoordTrans
@@ -1,62 +0,0 @@
#pragma once
#include <LtsCoordinate/ltscoordinate_export.h>
#include <Core/core.hpp>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QtCore/QDebug>
#include <QtCore/QPointF>
#include <QtCore/QVector>
#include <iosfwd>
#include <unordered_map>
#include <utility>
#include <vector>
namespace LtsCoordTrans
{
class LTSCOORDINATE_EXPORT LtsCoordVector
{
public:
explicit LtsCoordVector(const std::vector<Core::GeoPosition>& geo_coordinates);
int size() const
{
return m_geo_coordinates.size();
}
const std::vector<Core::GeoPosition>& geoPosition() const
{
return m_geo_coordinates;
}
const std::vector<LtsCoordTrans::UtmCoordinate>& toStdUtmCoord(int zone) const;
const QVector<LtsCoordTrans::UtmCoordinate>& toQVectorUtmCoord(int zone) const;
const QVector<QPointF>& toQVectorQPointF(int zone) const;
protected:
void convertGeoToStdUtm(int zone) const;
void convertGeoToQVectorUtm(int zone) const;
void convertGeoToQVectorQPointF(int zone) const;
template <typename T> void convertGeoToUtmInternal(int zone, T& utmVectorRef) const
{
utmVectorRef.clear();
utmVectorRef.reserve(m_geo_coordinates.size());
for (const auto& geoCoord : std::as_const(m_geo_coordinates))
utmVectorRef.push_back(LtsCoordTrans::UtmCoordinate::fromGeographic(geoCoord.lon, geoCoord.lat, zone));
}
protected:
std::vector<Core::GeoPosition> m_geo_coordinates;
mutable std::unordered_map<int, std::vector<LtsCoordTrans::UtmCoordinate>> m_utmMapStd;
mutable std::unordered_map<int, QVector<LtsCoordTrans::UtmCoordinate>> m_utmMapQVector;
mutable std::unordered_map<int, QVector<QPointF>> m_utmMapQVectorQPointF;
};
} // namespace LtsCoordTrans
@@ -1,143 +0,0 @@
#include "lts_coordinate.h"
#include <QtCore/QLineF>
#include <iomanip>
namespace LtsCoordTrans
{
static long s_conversion_counter = 0;
std::function<double(double, double, double, bool*)> LtsCoordinate::m_getElevationCB;
void LtsCoordinate::setElevationCallback(const elevationCbFunc& elevationCB)
{
m_getElevationCB = elevationCB;
}
bool LtsCoordinate::hasElevationCallback()
{
return m_getElevationCB != nullptr;
}
double LtsCoordinate::elevationAt(double defaultElevation, bool* bIsCovered) const
{
if (!bIsCovered)
if (!std::isnan(m_elevation))
return m_elevation;
if (m_getElevationCB)
m_elevation = m_getElevationCB(m_geo.lon, m_geo.lat, defaultElevation, bIsCovered);
return m_elevation;
}
void LtsCoordinate::setZValueAccordingElevation(double defaultElevation)
{
m_geo.z = elevationAt(defaultElevation);
}
double LtsCoordinate::elevationAt(const LtsCoordinate& coordinate, double defaultElevation, bool* bIsCovered)
{
return coordinate.elevationAt(defaultElevation, bIsCovered);
}
void LtsCoordinate::replaceZValueByElevationIfValue(double zValueToBeReplaced, double defaultElevation)
{
if ((std::isnan(zValueToBeReplaced) && std::isnan(m_geo.z)) || qFuzzyCompare(m_geo.z, zValueToBeReplaced))
setZValueAccordingElevation(defaultElevation);
}
double LtsCoordinate::angleDegreeUtm2D(const LtsCoordinate& coord1, const LtsCoordinate& coord2)
{
const LtsCoordTrans::UtmCoordinate& utm1 = coord1.utmCoordinate();
const LtsCoordTrans::UtmCoordinate& utm2 = coord2.utmCoordinateDiffZone(utm1.zone);
const QPointF& offsetUtm = utm2.toPoint() - utm1.toPoint();
QLineF poly(QPointF(), offsetUtm);
const double angle = poly.angle();
return fmod(angle + 90.0, 360);
}
const UtmCoordinate& LtsCoordinate::utmCoordinateInternal(int zone) const
{
if (zone == -1)
{
if (!hasUtm())
convertGeoToUtm();
return m_utm2D;
}
utmCoordinateDiffZone(zone);
return m_utm2D_differentZone;
}
void LtsCoordinate::convertGeoToUtm() const
{
int zone = -1;
if (std::isnan(m_geo.lon) || std::isnan(m_geo.lat))
{
fprintf(stderr, "Error while fromGeographic, value is NaN\n");
m_utm2D = LtsCoordTrans::UtmCoordinate();
return;
}
#ifdef LTS_DEBUG
++s_conversion_counter;
#endif
m_utm2D = LtsCoordTrans::UtmCoordinate::fromGeographic(m_geo.lon, m_geo.lat, zone);
Q_ASSERT_X(m_utm2D.zone > 0, "LtsCoordinate", "zone has to be set");
}
QString LtsCoordinate::mgrs(int prec, bool withSpaces) const
{
utmCoordinateInternal();
QString tmp = m_utm2D.mgrs(prec, withSpaces);
return tmp;
}
QString LtsCoordinate::mgrsWithZ(int prec, bool withSpaces) const
{
double ele = elevationAt();
QString tmp = mgrs(prec, withSpaces) + QStringLiteral(" z=") + QString::number(ele, 'f', 2);
return tmp;
}
QString LtsCoordinate::toString(bool fullAndExpensive) const
{
if (fullAndExpensive)
{
utmCoordinateInternal();
elevationAt();
}
std::ostringstream stream;
stream << "LtsCoordinate(m_geo=" << m_geo << ", m_utm2D.x " << m_utm2D.x << ", m_utm2D.y " << m_utm2D.y
<< ", m_utm2D.zone " << m_utm2D.zone;
if (!qFuzzyIsNull(m_utm2D.x) || !qFuzzyIsNull(m_utm2D.y))
stream << ", m_utm2D.mgrs '" << m_utm2D.mgrs().toUtf8().constData() << "'";
stream << ", elevation " << std::fixed << std::setprecision(3) << m_elevation << ")";
return QString::fromStdString(stream.str());
}
void LtsCoordinate::resetConversionCounter()
{
s_conversion_counter = 0;
}
long LtsCoordinate::getConversionCounter()
{
return s_conversion_counter;
}
std::ostream& operator<<(std::ostream& stream, const LtsCoordinate& value)
{
stream << value.toString().toStdString();
return stream;
}
} // namespace LtsCoordTrans
@@ -1,297 +0,0 @@
#pragma once
#include <LtsCoordinate/ltscoordinate_export.h>
#include <Core/core.hpp>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtils/LtsUtilsCore/qpoint3d.h>
#include <QtCore/QDebug>
#include <QtCore/QPointF>
#include <functional>
#include <iosfwd>
#include <type_traits>
#include <utility>
namespace LtsCoordTrans
{
using UtmCoordinate2D = LtsCoordTrans::UtmCoordinate;
using QPoint3D = LtsUtils::QPoint3D;
class LTSCOORDINATE_EXPORT LtsCoordinate
{
public:
explicit LtsCoordinate(double lon = 0, double lat = 0, double z = std::nan(""))
: m_geo(Core::GeoPosition(lon, lat, z))
{
}
explicit LtsCoordinate(const QPointF& lonLat, double z = std::nan(""))
: m_geo(Core::GeoPosition(lonLat.x(), lonLat.y(), z))
{
}
explicit LtsCoordinate(const Core::GeoPosition& geoPos) : m_geo(geoPos) {}
explicit LtsCoordinate(const Core::GeoPosition2D& geoPos2D) : m_geo(geoPos2D.lon, geoPos2D.lat, std::nan("")) {}
LtsCoordinate(const LtsCoordTrans::UtmCoordinate& utm2D, double zValue) = delete;
static LtsCoordinate fromGeoPosition(double lon, double lat, double z = std::nan(""))
{
return LtsCoordinate(lon, lat, z);
}
static LtsCoordinate fromUtmCoordinate(const LtsCoordTrans::UtmCoordinate& utm2D, double zValue)
{
Q_ASSERT_X(utm2D.zone > 0, "LtsCoordinate", "zone has to be set");
const QPointF& geoPnt = utm2D.geographic();
return LtsCoordinate(geoPnt.x(), geoPnt.y(), zValue);
}
static LtsCoordinate fromUtm(const QPointF& utm2d, int zone, double zValue)
{
const LtsCoordTrans::UtmCoordinate utmCoord(zone, utm2d.x(), utm2d.y());
const QPointF& geoPnt = utmCoord.geographic();
return LtsCoordinate(geoPnt.x(), geoPnt.y(), zValue);
}
bool isValid() const
{
return hasGeo() || hasUtm();
}
bool hasGeo() const
{
return !(valueIsNullOrNan(m_geo.lon) || valueIsNullOrNan(m_geo.lat));
}
bool hasUtm() const
{
return m_utm2D.isValid();
}
static bool valueIsNullOrNan(double value)
{
return qFuzzyIsNull(value) || std::isnan(value);
}
QString mgrs(int prec = 5, bool withSpaces = true) const;
QString mgrsWithZ(int prec = 5, bool withSpaces = true) const;
QString toString(bool fullAndExpensive = false) const;
static void resetConversionCounter();
static long getConversionCounter();
const Core::GeoPosition& geoPosition() const
{
return m_geo;
}
Core::GeoPosition2D geoPosition2D() const
{
return Core::GeoPosition2D(m_geo.lon, m_geo.lat);
}
const LtsCoordTrans::UtmCoordinate& utmCoordinate() const
{
if (!hasUtm())
convertGeoToUtm();
return m_utm2D;
}
double& rz()
{
return m_geo.z;
}
double z() const
{
return m_geo.z;
}
void setZ(double value)
{
m_geo.z = value;
}
LtsCoordTrans::UtmCoordinate utmCoordinateDiffZone(int zone) const
{
m_utm2D_differentZone = LtsCoordTrans::UtmCoordinate::fromGeographic(m_geo.lon, m_geo.lat, zone);
return m_utm2D_differentZone;
}
template <typename T> T toUtm(int zone = -1) const
{
T obj;
helperToUtm(obj, zone);
return obj;
}
QPointF toUtmPointF() const
{
return toUtm<QPointF>();
}
QPoint3D toUtmPoint3D() const
{
return toUtm<QPoint3D>();
}
QPointF toGeoPointF() const
{
return QPointF(m_geo.lon, m_geo.lat);
}
QPoint3D toGeoPoint3D() const
{
return QPoint3D(m_geo.lon, m_geo.lat, m_geo.z);
}
UtmCoordinate3D toUtmCoordinate3D() const
{
return UtmCoordinate3D(utmCoordinate(), m_geo.z);
}
using elevationCbFunc = std::function<double(double, double, double, bool*)>;
static void setElevationCallback(const elevationCbFunc& elevationCB);
static bool hasElevationCallback();
double elevationAt(double defaultElevation = 0.0, bool* bIsCovered = nullptr) const;
void setZValueAccordingElevation(double defaultElevation = 0.0);
static double elevationAt(const LtsCoordinate& coordinate, double defaultElevation = 0.0,
bool* bIsCovered = nullptr);
void replaceZValueByElevationIfValue(double zValueToBeReplaced = std::nan(""), double defaultElevation = 0.0);
double heightOverElevation() const
{
return m_geo.z - elevationAt();
}
double distanceUtm2D(const LtsCoordinate& other) const
{
const LtsCoordTrans::UtmCoordinate& thisUtm2D = utmCoordinate();
const LtsCoordTrans::UtmCoordinate& otherUtm2D = other.utmCoordinateDiffZone(m_utm2D.zone);
return LtsCoordTrans::distance(thisUtm2D, otherUtm2D);
}
double distanceUtm3D(const LtsCoordinate& other) const
{
const QPoint3D& thisUtm3D = toUtm<QPoint3D>();
const QPoint3D& otherUtm3D = other.toUtm<QPoint3D>(m_utm2D.zone);
const double dist3D = (thisUtm3D - otherUtm3D).length();
Q_ASSERT_X(!std::isnan(dist3D), "LtsCoordinate", "ERROR, distance3D is NaN");
return dist3D;
}
static double angleDegreeUtm2D(const LtsCoordinate& coord1, const LtsCoordinate& coord2);
protected:
void clearGeo() const
{
clearGeoLonLat();
m_geo.z = std::nan("");
}
void clearGeoLonLat() const
{
m_geo.lon = 0;
m_geo.lat = 0;
}
void clearUtm() const
{
m_utm2D = m_utm2D_differentZone = UtmCoordinate2D();
}
void clearElevation() const
{
m_elevation = std::nan("");
}
void clear() const
{
clearGeo();
clearUtm();
clearElevation();
}
const LtsCoordTrans::UtmCoordinate& utmCoordinateInternal(int zone = -1) const;
void convertGeoToUtm() const;
protected:
void helperToUtm(QPointF& pntRef, int zone = -1) const
{
const auto& utmTemp = utmCoordinateInternal(zone);
pntRef.rx() = utmTemp.x;
pntRef.ry() = utmTemp.y;
}
void helperToUtm(QPoint3D& pntRef, int zone = -1) const
{
const auto& utmTemp = utmCoordinateInternal(zone);
pntRef.rx() = utmTemp.x;
pntRef.ry() = utmTemp.y;
pntRef.rz() = m_geo.z;
}
protected:
mutable Core::GeoPosition m_geo;
mutable UtmCoordinate2D m_utm2D;
mutable UtmCoordinate2D m_utm2D_differentZone;
mutable double m_elevation{std::nan("")};
static elevationCbFunc m_getElevationCB;
};
class LTSCOORDINATE_EXPORT LtsCoordinateNoImplicitCopy : public LtsCoordinate
{
public:
using BaseClass = LtsCoordinate;
LtsCoordinateNoImplicitCopy() = default;
explicit LtsCoordinateNoImplicitCopy(const LtsCoordinateNoImplicitCopy&) = default;
~LtsCoordinateNoImplicitCopy() = default;
explicit LtsCoordinateNoImplicitCopy(const QPointF& lonLat, double z = std::nan(""))
: BaseClass(Core::GeoPosition(lonLat.x(), lonLat.y(), z))
{
}
explicit LtsCoordinateNoImplicitCopy(const Core::GeoPosition& geoPos) : BaseClass(geoPos) {}
explicit LtsCoordinateNoImplicitCopy(const Core::GeoPosition2D& geoPos2D) : BaseClass(geoPos2D) {}
void updateGeoPosition(const Core::GeoPosition& geoPos) const
{
clear();
m_geo = geoPos;
}
};
template <typename T_OUT, typename T_IN> T_OUT convert(const T_IN& contIn)
{
T_OUT out;
out.reserve(contIn.size());
for (const Core::GeoPosition& posGeo : std::as_const(contIn))
out.push_back(LtsCoordinate(posGeo));
return out;
}
LTSCOORDINATE_EXPORT std::ostream& operator<<(std::ostream& stream, const LtsCoordinate& value);
} // namespace LtsCoordTrans
@@ -1,52 +0,0 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Make ${Qt6_Gui} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui)
set(Qt6_Gui ${Qt6_Gui})
else()
set(Qt6_Gui "")
endif()
NGL_ADD_LIBRARY2(WS SV HLA LtsElevation SHARED
lts_elevation_manager.cpp
lts_elevation_manager.h
logger.cpp
logger.h
)
NGL_TARGET_INCLUDE_DIRECTORIES(LtsElevation
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_SOURCE_DIR}/Lts/Common
${CMAKE_SOURCE_DIR}/Lts/Common/LtsUtils
${CMAKE_SOURCE_DIR}/Lts/Viewer/SituationMap
${CMAKE_SOURCE_DIR}/Lts/Viewer/SituationMap/Common
)
if(QT6_GUI_AVAILABLE)
NGL_TARGET_LINK_LIBRARIES(LtsElevation
PRIVATE
NGLLogger
## LtsSessionDataDds
PUBLIC
LtsUtilsCoreHeaderOnly
Qt6::Core
${Qt6_Gui}
)
endif()
if(BUILD_RTI)
# All ElevationMapManager / resource_path / exm-dtc usages live inside
# #ifdef BUILD_RTI in lts_elevation_manager.cpp (Qt5 dependencies restored).
target_link_libraries(LtsElevation PRIVATE
AsmDataPluginInterface
ExmDataPluginInterface
situationMapCommon
situationMapElevationMap
)
endif()
ADD_SUBDIRECTORY(UnitTest_Elevation)
@@ -1,33 +0,0 @@
# Make ${Qt6_Gui} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui)
set(Qt6_Gui ${Qt6_Gui})
else()
set(Qt6_Gui "")
endif()
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
# External dependency - missing: situationMapCommon, situationMapElevationMap
# Test disabled until external dependencies are available
# IF(BUILD_TESTING AND TARGET LtsElevation)
# ADD_EXECUTABLE(commenUnitTestElevation ${LinkedSources} ${UnitTestSources} )
#
# TARGET_LINK_LIBRARIES(commenUnitTestElevation
# NGLLogger
# nglcoreapplication
# LtsElevation
# LtsCoordTrans
# situationMapCommon
# situationMapElevationMap
# Qt6::Core
# ${Qt6_Gui}
# Qt6::Test)
#
# ADD_TEST(NAME commenUnitTestElevation COMMAND $<TARGET_FILE:commenUnitTestElevation>)
# ENDIF()
@@ -1,198 +0,0 @@
#include "elevation_unit_test.h"
#include <ElevationMap/elevation_map_manager.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtTest/QTest>
#include <nglapplication/nglcoreapplication.h>
Q_DECLARE_METATYPE(LtsUtils::QPoint3D);
Q_DECLARE_METATYPE(LtsElevation::LineOfSight);
#include <iostream>
static const int s_id{4711};
static LtsElevation::LtsElevationManager* s_eleMgr{nullptr};
class MyLtsElevationManagerBootstrap : public LtsElevation::LtsElevationManager
{
using BaseClass = LtsElevation::LtsElevationManager;
public:
static LtsElevationManager* createInstanceUnitTest(int id, const QStringList& filePaths_rough_to_fine)
{
return BaseClass::createInstanceUnitTest(id, filePaths_rough_to_fine);
}
};
UnitTestElevation::UnitTestElevation()
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTestElevation"))
{
}
void UnitTestElevation::initTestCase()
{
QString filePath(QStringLiteral("/usr/share/arcgis/resources/elevation/Altmark_dt1.tif"));
s_eleMgr = MyLtsElevationManagerBootstrap::createInstanceUnitTest(s_id, {filePath});
const bool bIsValid = s_eleMgr->isValid();
std::cerr << "myBootstrapMgr->isValid() 1 = " << bIsValid << std::endl;
{
s_eleMgr->waitForInitialization();
const bool bIsValid = s_eleMgr->isValid();
std::cerr << "myBootstrapMgr->isValid() 2 = " << bIsValid << std::endl;
}
}
void UnitTestElevation::init()
{
QVERIFY2(s_eleMgr, "static LtsElevationManager does not exist");
}
void UnitTestElevation::test_elevation_data()
{
QTest::addColumn<QPointF>("lonLat");
QTest::addColumn<double>("elevationRef");
QTest::newRow("elevation 1 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 70000 10000")).geographic() << 75.0;
QTest::newRow("elevation 2 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 72000 13000")).geographic() << 80.0;
QTest::newRow("elevation 3 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 76925 18625")).geographic() << 92.0;
}
void UnitTestElevation::test_elevation()
{
QFETCH(QPointF, lonLat);
QFETCH(double, elevationRef);
double elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y());
QVERIFY2(qFuzzyCompare(elevationRef, elevation), " got wrong elevation result");
}
void UnitTestElevation::test_elevation_isCovered()
{
const QPointF& lonLat1 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 70000 10000")).geographic();
const QPointF& lonLat2 = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 60000 10000")).geographic();
const double defaultElevationValue = -123;
{
bool bIsCovered = false;
s_eleMgr->elevation(lonLat1.x(), lonLat1.y(), defaultElevationValue, &bIsCovered);
QVERIFY2(bIsCovered, " error on bIsCovered");
}
{
bool bIsCovered = true;
s_eleMgr->elevation(lonLat2.x(), lonLat2.y(), defaultElevationValue, &bIsCovered);
QVERIFY2(!bIsCovered, " error on bIsCovered");
}
}
void UnitTestElevation::test_elevation_defaultValue()
{
const QPointF& lonLat = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 60000 10000")).geographic();
double elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y());
QVERIFY2(qFuzzyCompare(elevation, std::numeric_limits<double>::min()), " error on defaultValue");
{
const double defaultValue = 100;
elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y(), defaultValue);
QVERIFY2(qFuzzyCompare(elevation, defaultValue), " error on defaultValue");
}
{
const double defaultValue = std::numeric_limits<double>::max();
elevation = s_eleMgr->elevation(lonLat.x(), lonLat.y(), defaultValue);
QVERIFY2(qFuzzyCompare(elevation, defaultValue), " error on defaultValue");
}
}
void UnitTestElevation::test_lineOfSightCalculation_data()
{
QTest::addColumn<QPointF>("startGeo2D");
QTest::addColumn<double>("startObjH");
QTest::addColumn<QPointF>("endGeo2D");
QTest::addColumn<double>("endObjH");
QTest::addColumn<LtsElevation::LineOfSight>("expectedResult");
QTest::newRow("losDataset 1 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 60000 10000")).geographic() << 1.0
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 60000 60000")).geographic() << 1.0
<< LtsElevation::LineOfSight::LOS_ERROR;
QTest::newRow("losDataset 2 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 1.0
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 1.0
<< LtsElevation::LineOfSight::LOS_INVISIBLE;
QTest::newRow("losDataset 3 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 20.0
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 20.0
<< LtsElevation::LineOfSight::LOS_VISIBLE;
QTest::newRow("losDataset 4 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << -1.0
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 1.0
<< LtsElevation::LineOfSight::LOS_ERROR;
QTest::newRow("losDataset 5 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 1.0
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << -1.0
<< LtsElevation::LineOfSight::LOS_ERROR;
QTest::newRow("losDataset 6 ")
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77200 17600")).geographic() << 0.0
<< LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77600 17600")).geographic() << 0.0
<< LtsElevation::LineOfSight::LOS_INVISIBLE;
}
void UnitTestElevation::test_lineOfSightCalculation()
{
QFETCH(QPointF, startGeo2D);
QFETCH(double, startObjH);
QFETCH(QPointF, endGeo2D);
QFETCH(double, endObjH);
QFETCH(LtsElevation::LineOfSight, expectedResult);
LtsUtils::QPoint3D start3D = LtsUtils::QPoint3D::fromQPointF(startGeo2D);
start3D.rz() = startObjH;
start3D.rz() += s_eleMgr->elevation(start3D.x(), start3D.y());
LtsUtils::QPoint3D end3D = LtsUtils::QPoint3D::fromQPointF(endGeo2D);
end3D.rz() = endObjH;
end3D.rz() += s_eleMgr->elevation(end3D.x(), end3D.y());
LtsElevation::LineOfSight los = s_eleMgr->lineOfSightCalculation(start3D, end3D);
NOTICE_LOG(m_logger) << "los = " << los;
{
LtsElevation::LineOfSight los2 = s_eleMgr->lineOfSightCalculation(startGeo2D, startObjH, endGeo2D, endObjH);
QVERIFY2(los == los2, " got different lineOfSightCalculation result");
}
QVERIFY2(los == expectedResult, " got lineOfSightCalculation result");
}
int main(int argc, char* argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTestElevation", "log4cxxElevationUnitTest.props");
UnitTestElevation tc;
int ret = QTest::qExec(&tc, argc, argv);
if (s_eleMgr)
{
s_eleMgr->cleanupAll();
}
return ret;
}
@@ -1,38 +0,0 @@
#pragma once
#include <ElevationMap/elevation_map_manager.h>
#include <LtsElevation/lts_elevation_manager.h>
#include <NGLLogger/INGLLogger.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtTest/QTest>
using LOGGER_T = NGL::INGLLogger&;
class UnitTestElevation : public QObject
{
Q_OBJECT
public:
UnitTestElevation();
private Q_SLOTS:
void initTestCase();
void init();
void test_elevation_data();
void test_elevation();
void test_elevation_isCovered();
void test_elevation_defaultValue();
void test_lineOfSightCalculation_data();
void test_lineOfSightCalculation();
private:
NGL::INGLLogger& m_logger;
};
@@ -1,30 +0,0 @@
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace LtsElevation
{
LOGGER_T getLogger(const char* name)
{
static const char ns[] = "Lts.Elevation.";
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 LtsElevation
@@ -1,17 +0,0 @@
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace LtsElevation
{
typedef NGL::INGLLogger& LOGGER_T;
LOGGER_T getLogger(const char* name);
inline LOGGER_T getLogger(const QString& loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace LtsElevation
@@ -1,587 +0,0 @@
#include "lts_elevation_manager.h"
#ifdef BUILD_RTI
#include <AssetManagement/asm_data_plugin_interface.h>
#include <ExerciseManagement/exm_data_plugin_interface.h>
#include <Viewer/SituationMap/ElevationMap/elevation_map_manager.h>
#include <Viewer/SituationMap/Common/resource_path.h>
#include <QDir>
#include <QHash>
#include <QSharedPointer>
#include <QUrl>
static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_UNDEFINED ==
(int)LtsElevation::LineOfSight::LOS_UNDEFINED,
"LineOfSight mismatch");
static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_ERROR ==
(int)LtsElevation::LineOfSight::LOS_ERROR,
"LineOfSight mismatch");
static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_VISIBLE ==
(int)LtsElevation::LineOfSight::LOS_VISIBLE,
"LineOfSight mismatch");
static_assert((int)SituationMap::ElevationMapManager::LineOfSight::LOS_INVISIBLE ==
(int)LtsElevation::LineOfSight::LOS_INVISIBLE,
"LineOfSight mismatch");
#endif
namespace LtsElevation
{
class LtsElevationManager;
using LtsElevatioMgrMapTrainingAreaId = QMap<int, LTS_ELEVATION_MANAGER_TYPE>;
static LtsElevatioMgrMapTrainingAreaId& ltsElevatioMgrMapTrainingAreaId()
{
static LtsElevatioMgrMapTrainingAreaId s_ltsElevatioMgrMapTrainingAreaId;
return s_ltsElevatioMgrMapTrainingAreaId;
}
static void cleanupLtsElevatioMgrMapTrainingAreaId()
{
qDeleteAll(ltsElevatioMgrMapTrainingAreaId());
ltsElevatioMgrMapTrainingAreaId().clear();
}
LtsElevatioMgrMapTrainingAreaId& getLtsElevationMgrMapTrainingAreaId()
{
if (ltsElevatioMgrMapTrainingAreaId().isEmpty())
qAddPostRoutine(cleanupLtsElevatioMgrMapTrainingAreaId);
return ltsElevatioMgrMapTrainingAreaId();
}
static long s_counter{0};
class LtsElevationManager::Private
{
public:
Private() = default;
~Private()
{
#ifdef BUILD_RTI
delete m_elevationManager;
#endif
}
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
#ifdef BUILD_RTI
std::shared_ptr<AssetManagement::Interface::Dtc> m_asmDtc;
SituationMap::ElevationMapManager* m_elevationManager{nullptr};
#endif
QStringList m_filePaths_rough_to_fine;
};
LtsElevationManager::LtsElevationManager()
: QObject(nullptr)
, d(new Private)
, m_logger(getLogger("LtsElevationManager"))
{
++s_counter;
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START, s_counter=" << s_counter << " this=" << this;
TRACE_LOG(m_logger) << Q_FUNC_INFO << " END, s_counter=" << s_counter << " this=" << this;
}
LtsElevationManager::~LtsElevationManager()
{
--s_counter;
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START, s_counter=" << s_counter << " this=" << this;
delete d;
TRACE_LOG(m_logger) << Q_FUNC_INFO << " END, s_counter=" << s_counter << " this=" << this;
}
LTS_ELEVATION_MANAGER_TYPE
LtsElevationManager::createInstance(int exerciseId, const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc,
#ifdef BUILD_RTI
const std::shared_ptr<AssetManagement::Interface::Dtc>& asmDtc
#else
const std::shared_ptr<void>& asmDtc
#endif
)
{
LOGGER_T logger = getLogger("LtsElevationManager");
TRACE_LOG(logger) << Q_FUNC_INFO << " START, exerciseId=" << exerciseId;
if (exerciseId < 0)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " exerciseId invalid, exerciseId=" << exerciseId;
return nullptr;
}
// Qt5 logic restored (the qt6 port had dropped the exerciseId->trainingAreaId
// caching and always returned nullptr here).
#ifdef BUILD_RTI
int trainingAreaId = -1;
// remember for future calls
// (so createInstance should work WITHOUT dtcs)
// Idea: MainHandler: createInstance with exerciseId and valid dtcs
// Plugin or simular: createInstance with exerciseId and no dtcs
static QHash<int, int> s_exIdToTaIdStore;
const auto exTaIt = s_exIdToTaIdStore.find(exerciseId);
if (exTaIt != s_exIdToTaIdStore.end())
{
// CASE 1:
// already created:
trainingAreaId = *exTaIt;
}
else
{
// CASE 2:
// ... or create on demand:
if (!exmDtc || !asmDtc)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " no dtcs for exerciseId=" << exerciseId
<< ", asmDtc=" << asmDtc
<< ", exmDtc=" << exmDtc;
return nullptr;
}
// exId to taId
trainingAreaId = exerciseIdToTrainingAreaId(exerciseId, exmDtc);
if (trainingAreaId <= 0)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " Error getting trainingAreaId"
<< ", exerciseId=" << exerciseId
<< ", trainingAreaId=" << trainingAreaId;
return nullptr;
}
// store and remember:
s_exIdToTaIdStore[exerciseId] = trainingAreaId;
}
// CASE 1: Following should work even with invalid asmDtc:
LTS_ELEVATION_MANAGER_TYPE temp = createInstanceForTrainingAreaId(trainingAreaId, asmDtc);
if (!temp)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " Error on createInstanceForTrainingAreaId"
<< ", exerciseId=" << exerciseId
<< ", trainingAreaId=" << trainingAreaId;
return nullptr;
}
// add exmDtc
temp->initDDS(exmDtc, asmDtc);
TRACE_LOG(logger) << Q_FUNC_INFO << " END, exerciseId=" << exerciseId;
return temp;
#else
Q_UNUSED(exmDtc);
Q_UNUSED(asmDtc);
ERROR_LOG(logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, cannot create instance";
return nullptr;
#endif
}
LTS_ELEVATION_MANAGER_TYPE LtsElevationManager::getInstance(int exerciseId)
{
return LtsElevationManager::createInstance(exerciseId);
}
#ifdef BUILD_RTI
LTS_ELEVATION_MANAGER_TYPE
LtsElevationManager::createInstanceForTrainingAreaId(int trainingAreaId,
const std::shared_ptr<AssetManagement::Interface::Dtc>& asmDtc)
{
LOGGER_T logger = getLogger("LtsElevationManager");
TRACE_LOG(logger) << Q_FUNC_INFO << " START, trainingAreaId=" << trainingAreaId;
if (trainingAreaId < 0)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " trainingAreaId invalid, trainingAreaId=" << trainingAreaId;
return nullptr;
}
LtsElevatioMgrMapTrainingAreaId& map = getLtsElevationMgrMapTrainingAreaId();
QMap<int, LTS_ELEVATION_MANAGER_TYPE>::iterator it = map.find(trainingAreaId);
if (it != map.end())
{
DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances: " << map.size();
return it.value();
}
if (!asmDtc)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " no dtcs for trainingAreaId=" << trainingAreaId << ", asmDtc=" << asmDtc;
return nullptr;
}
LTS_ELEVATION_MANAGER_TYPE temp = new LtsElevationManager();
temp->initDDS(nullptr, asmDtc);
const QStringList& filenames = temp->getFilenamesForTrainingArea(trainingAreaId);
if (filenames.isEmpty())
{
ERROR_LOG(logger) << Q_FUNC_INFO << " no filenames for trainingAreaId=" << trainingAreaId;
delete temp;
return nullptr;
}
bool bInitFilenames = temp->init(filenames);
if (!bInitFilenames)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " bInitFilenames failed for trainingAreaId=" << trainingAreaId;
delete temp;
return nullptr;
}
map.insert(trainingAreaId, temp);
DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances now: " << map.size();
TRACE_LOG(logger) << Q_FUNC_INFO << " END, trainingAreaId=" << trainingAreaId;
return map[trainingAreaId];
}
#endif
LTS_ELEVATION_MANAGER_TYPE LtsElevationManager::createInstanceUnitTest(int id,
const QStringList& filePaths_rough_to_fine)
{
LOGGER_T logger = getLogger("LtsElevationManager");
LtsElevatioMgrMapTrainingAreaId& map = getLtsElevationMgrMapTrainingAreaId();
QMap<int, LTS_ELEVATION_MANAGER_TYPE>::iterator it = map.find(id);
if (it != map.end())
{
DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances: " << map.size();
return it.value();
}
LTS_ELEVATION_MANAGER_TYPE temp = new LtsElevationManager();
bool bInitFilenames = temp->init(filePaths_rough_to_fine);
if (!bInitFilenames)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " bInitFilenames failed for id=" << id;
delete temp;
return nullptr;
}
map.insert(id, temp);
DEBUG_LOG(logger) << " LtsElevationMgrMap contains instances now: " << map.size();
TRACE_LOG(logger) << Q_FUNC_INFO << " END, trainingAreaId=" << id;
return map[id];
}
void LtsElevationManager::cleanupAll()
{
LOGGER_T logger = getLogger("LtsElevationManager");
TRACE_LOG(logger) << Q_FUNC_INFO << " START";
cleanupLtsElevatioMgrMapTrainingAreaId();
TRACE_LOG(logger) << Q_FUNC_INFO << " END";
}
#ifdef BUILD_RTI
void LtsElevationManager::initDDS(const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc,
const std::shared_ptr<AssetManagement::Interface::Dtc>& asmDtc)
{
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START";
d->m_exmDtc = exmDtc;
if (d->m_exmDtc)
d->m_exmDtc->waitForHistoricalDataExercise(1000);
d->m_asmDtc = asmDtc;
if (d->m_asmDtc)
d->m_asmDtc->waitForHistoricalDataTrainingArea(1000);
TRACE_LOG(m_logger) << Q_FUNC_INFO << " END";
}
#else
void LtsElevationManager::initDDS(const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc,
const std::shared_ptr<void>& asmDtc)
{
Q_UNUSED(exmDtc);
Q_UNUSED(asmDtc);
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START (BUILD_RTI=OFF, no DDS init)";
// BUILD_RTI=OFF: No DDS initialization
TRACE_LOG(m_logger) << Q_FUNC_INFO << " END";
}
#endif
#ifdef BUILD_RTI
QStringList LtsElevationManager::getFilenamesForExercise(int exerciseId)
{
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START";
if (!d->m_exmDtc || !d->m_asmDtc)
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << " no dtcs"
<< ", exerciseId=" << exerciseId;
return {};
}
int trainingAreaId = exerciseIdToTrainingAreaId(exerciseId, d->m_exmDtc);
if (trainingAreaId <= 0)
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << " Error getting trainingAreaId"
<< ", exerciseId=" << exerciseId << ", trainingAreaId=" << trainingAreaId;
return {};
}
QStringList filePaths_rough_to_fine = getFilenamesForTrainingArea(trainingAreaId);
TRACE_LOG(m_logger) << Q_FUNC_INFO
<< " END, filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(";"));
return filePaths_rough_to_fine;
}
QStringList LtsElevationManager::getFilenamesForTrainingArea(int trainingAreaId)
{
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START";
if (!d->m_asmDtc)
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << " no dtcs"
<< ", trainingAreaId=" << trainingAreaId;
return {};
}
QStringList filePaths_rough_to_fine;
{
d->m_asmDtc->waitForHistoricalDataTrainingArea(1000);
const AssetManagement::TrainingArea& ta = d->m_asmDtc->trainingArea(trainingAreaId);
if (ta.id != trainingAreaId)
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << " Error on s_asmDtc->trainingArea"
<< ", trainingAreaId=" << trainingAreaId;
return {};
}
const std::vector<AssetManagement::MapDescription>& mdVec = ta.elevation;
filePaths_rough_to_fine.reserve(mdVec.size());
for (const AssetManagement::MapDescription& md : std::as_const(mdVec))
{
QUrl url(QString::fromStdString(md.url));
QString filePathAbs = url.toLocalFile();
if (QDir::isRelativePath(filePathAbs))
filePathAbs = SituationMap::getResourcePath() + QStringLiteral("/elevation/") + filePathAbs;
filePaths_rough_to_fine.append(filePathAbs);
}
}
TRACE_LOG(m_logger) << Q_FUNC_INFO
<< " END, filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(";"));
return filePaths_rough_to_fine;
}
#else
QStringList LtsElevationManager::getFilenamesForExercise(int /*exerciseId*/)
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, getFilenamesForExercise not available";
return {};
}
QStringList LtsElevationManager::getFilenamesForTrainingArea(int /*trainingAreaId*/)
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, getFilenamesForTrainingArea not available";
return {};
}
#endif
bool LtsElevationManager::init(const QStringList& filePaths_rough_to_fine)
{
#ifdef BUILD_RTI
delete d->m_elevationManager;
d->m_elevationManager = new SituationMap::ElevationMapManager();
int cnt = d->m_elevationManager->init(filePaths_rough_to_fine);
if (cnt != filePaths_rough_to_fine.size())
{
FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!";
FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!";
FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!";
FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!";
FATAL_LOG(m_logger) << Q_FUNC_INFO << " ERROR, not all files could be loaded !!!";
return false;
}
return true;
#else
Q_UNUSED(filePaths_rough_to_fine);
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, init not available";
return false;
#endif
}
#ifdef BUILD_RTI
int LtsElevationManager::exerciseIdToTrainingAreaId(int exerciseId,
const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc)
{
LOGGER_T logger = getLogger("LtsElevationManager");
if (!exmDtc)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " no dtcs"
<< ", exerciseId=" << exerciseId;
return -1;
}
int trainingAreaId = -1;
{
exmDtc->waitForHistoricalDataExercise(1000);
const ExerciseManagement::Exercise& exm = exmDtc->exercise(exerciseId);
if (exm.id != exerciseId)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " Error on m_exmDtc->exercise"
<< ", exerciseId=" << exerciseId;
return -1;
}
trainingAreaId = exm.trainingAreaId;
if (trainingAreaId <= 0)
{
ERROR_LOG(logger) << Q_FUNC_INFO << " Error getting trainingAreaId"
<< ", exerciseId=" << exerciseId << ", trainingAreaId=" << trainingAreaId;
return -1;
}
}
return trainingAreaId;
}
#else
int LtsElevationManager::exerciseIdToTrainingAreaId(int exerciseId,
const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc)
{
Q_UNUSED(exerciseId);
Q_UNUSED(exmDtc);
LOGGER_T logger = getLogger("LtsElevationManager");
ERROR_LOG(logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, cannot convert exerciseId to trainingAreaId";
return -1;
}
#endif
bool LtsElevationManager::waitForInitialization()
{
#ifdef BUILD_RTI
if (!d->m_elevationManager)
return false;
return d->m_elevationManager->waitForInitialization();
#else
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, waitForInitialization not available";
return false;
#endif
}
bool LtsElevationManager::isValid() const
{
#ifdef BUILD_RTI
if (!d->m_elevationManager)
return false;
return d->m_elevationManager->isValid();
#else
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, isValid not available";
return false;
#endif
}
double LtsElevationManager::elevation(double lon_rad, double lat_rad, double defaultValue, bool* bIsCovered) const
{
#ifdef BUILD_RTI
if (!d->m_elevationManager)
return defaultValue;
return d->m_elevationManager->elevation(lon_rad, lat_rad, defaultValue, bIsCovered);
#else
Q_UNUSED(lon_rad);
Q_UNUSED(lat_rad);
Q_UNUSED(bIsCovered);
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, elevation not available";
return defaultValue;
#endif
}
LineOfSight LtsElevationManager::lineOfSightCalculation(const LtsUtils::QPoint3D& startPointGeo_absZ,
const LtsUtils::QPoint3D& endPointGeo_absZ)
{
#ifdef BUILD_RTI
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START"
<< ", startPointGeo_absZ = " << startPointGeo_absZ
<< ", endPointGeo_absZ = " << endPointGeo_absZ;
if (!isValid())
{
ERROR_LOG(m_logger) << Q_FUNC_INFO << " Error, LtsElevationManager not valid";
return LineOfSight::LOS_ERROR;
}
SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult =
d->m_elevationManager->lineOfSightCalculation(startPointGeo_absZ, endPointGeo_absZ);
LtsElevation::LineOfSight result = static_cast<LtsElevation::LineOfSight>(eleMapMgrResult);
TRACE_LOG(m_logger) << Q_FUNC_INFO << " END"
<< ", startPointGeo = " << startPointGeo_absZ << ", endPointGeo = " << endPointGeo_absZ
<< " -> LineOfSight " << result;
return result;
#else
Q_UNUSED(startPointGeo_absZ);
Q_UNUSED(endPointGeo_absZ);
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, lineOfSightCalculation not available";
return LineOfSight::LOS_ERROR;
#endif
}
LineOfSight LtsElevationManager::lineOfSightCalculation(const QPointF& startPointGeo, double startHat,
const QPointF& endPointGeo, double endHat)
{
#ifdef BUILD_RTI
TRACE_LOG(m_logger) << Q_FUNC_INFO << " START"
<< ", startPointGeo = " << startPointGeo << ", startHat = " << startHat
<< ", endPointGeo = " << endPointGeo << ", endHat = " << endHat;
SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult =
d->m_elevationManager->lineOfSightCalculation(startPointGeo, startHat, endPointGeo, endHat);
LtsElevation::LineOfSight result = static_cast<LtsElevation::LineOfSight>(eleMapMgrResult);
TRACE_LOG(m_logger) << Q_FUNC_INFO << " END"
<< ", startPointGeo = " << startPointGeo << ", startHat = " << startHat
<< ", endPointGeo = " << endPointGeo << ", endHat = " << endHat << " -> LineOfSight "
<< static_cast<int>(eleMapMgrResult);
return result;
#else
Q_UNUSED(startPointGeo);
Q_UNUSED(startHat);
Q_UNUSED(endPointGeo);
Q_UNUSED(endHat);
ERROR_LOG(m_logger) << Q_FUNC_INFO << " BUILD_RTI=OFF, lineOfSightCalculation not available";
return LineOfSight::LOS_ERROR;
#endif
}
const QString toString(const LineOfSight& value)
{
switch (value)
{
case LineOfSight::LOS_UNDEFINED:
return QStringLiteral("LOS_UNDEFINED");
case LineOfSight::LOS_ERROR:
return QStringLiteral("LOS_ERROR");
case LineOfSight::LOS_VISIBLE:
return QStringLiteral("LOS_VISIBLE");
case LineOfSight::LOS_INVISIBLE:
return QStringLiteral("LOS_INVISIBLE");
}
Q_ASSERT(false);
return QStringLiteral("LineOfSightERROR");
}
std::ostream& operator<<(std::ostream& stream, const LineOfSight value)
{
stream << toString(value).toStdString();
return stream;
}
} // namespace LtsElevation
@@ -1,113 +0,0 @@
#pragma once
#include "logger.h"
#include "ltselevation_export.h"
#include <LtsUtils/LtsUtilsCore/qpoint3d.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QMap>
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QStringList>
#include <iosfwd>
#include <memory>
namespace LtsSession
{
class LtsSessionDataDds;
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
} // namespace ExerciseManagement
#ifdef BUILD_RTI
namespace AssetManagement
{
namespace Interface
{
class Dtc;
}
} // namespace AssetManagement
#endif
namespace LtsElevation
{
enum class LineOfSight { LOS_UNDEFINED, LOS_ERROR, LOS_VISIBLE, LOS_INVISIBLE };
class LtsElevationManager;
using LTS_ELEVATION_MANAGER_TYPE = QPointer<LtsElevationManager>;
class LTSELEVATION_EXPORT LtsElevationManager : public QObject
{
public:
static LTS_ELEVATION_MANAGER_TYPE
createInstance(int exerciseId, const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc = {},
#ifdef BUILD_RTI
const std::shared_ptr<AssetManagement::Interface::Dtc>& asmDtc = {}
#else
const std::shared_ptr<void>& asmDtc = {}
#endif
);
static LTS_ELEVATION_MANAGER_TYPE getInstance(int exerciseId);
#ifdef BUILD_RTI
static LTS_ELEVATION_MANAGER_TYPE
createInstanceForTrainingAreaId(int trainingAreaId,
const std::shared_ptr<AssetManagement::Interface::Dtc>& asmDtc = nullptr);
#endif
static void cleanupAll();
virtual ~LtsElevationManager();
bool waitForInitialization();
bool isValid() const;
double elevation(double lon_rad, double lat_rad, double defaultValue = std::numeric_limits<double>::min(),
bool* bIsCovered = nullptr) const;
LineOfSight lineOfSightCalculation(const LtsUtils::QPoint3D& startPointGeo_absZ,
const LtsUtils::QPoint3D& endPointGeo_absZ);
LineOfSight lineOfSightCalculation(const QPointF& startPointGeo, double startHat, const QPointF& endPointGeo,
double endHat);
protected:
explicit LtsElevationManager();
void initDDS(const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc,
#ifdef BUILD_RTI
const std::shared_ptr<AssetManagement::Interface::Dtc>& asmDtc
#else
const std::shared_ptr<void>& asmDtc
#endif
);
bool init(const QStringList& filePaths_rough_to_fine);
static int exerciseIdToTrainingAreaId(int exerciseId,
const std::shared_ptr<ExerciseManagement::Interface::Dtc>& exmDtc);
QStringList getFilenamesForExercise(int exerciseId);
QStringList getFilenamesForTrainingArea(int trainingAreaId);
static LTS_ELEVATION_MANAGER_TYPE createInstanceUnitTest(int id, const QStringList& filePaths_rough_to_fine);
private:
class Private;
Private* d;
LOGGER_T m_logger;
};
LTSELEVATION_EXPORT const QString toString(const LtsElevation::LineOfSight& value);
LTSELEVATION_EXPORT std::ostream& operator<<(std::ostream& stream, const LtsElevation::LineOfSight value);
} // namespace LtsElevation
@@ -1,44 +0,0 @@
#
# Library which automatically provides Login/Logoff so that user data (rights for apps, etc.) is available before any app is started
#
# $Id$
# $HeadURL$
# $Author$
# Created by Mike Wille, SLSE, 2016-10-27
#
# LtsLogin
set(LtsLogin_SRCS
lts_login.cpp
logout_info_dialog.cpp
)
NGL_WRAP_UI(LtsLogin_SRCS
logout_info_dialog.ui
)
IF(BUILD_RTI)
NGL_ADD_LIBRARY2(WS LtsLogin SHARED ${LtsLogin_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsLogin
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsLogin
PRIVATE
LtsAppDescriptor
LtsPluginManager
LtsSession
LtsSessionData
LtsWidgets
LtsUtilsSettings
LtsUtilsCoreHeaderOnly
PUBLIC
OpmDataPluginInterface
SrmDataPluginInterface
AsmDataPluginInterface
Qt6::Core
)
ENDIF(BUILD_RTI)
@@ -1,92 +0,0 @@
#include "logout_info_dialog.h"
#include "ui_logout_info_dialog.h"
#include <QtCore/QTimer>
namespace LtsLogin
{
LogoutInformationDialog::LogoutInformationDialog(int timeOut, AssetManagement::ShutdownAction shutdownAction,
QWidget* parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal), m_ui(new Ui::LogoutInformationDialog),
m_timer(new QTimer(this)), m_timeOut(timeOut), m_secsLeft(timeOut)
{
m_ui->setupUi(this);
QString actionText;
switch (shutdownAction)
{
case AssetManagement::ShutdownAction::NoAction:
case AssetManagement::ShutdownAction::Logout:
actionText = tr("logout");
break;
case AssetManagement::ShutdownAction::Shutdown:
actionText = tr("shutdown");
break;
case AssetManagement::ShutdownAction::Reboot:
actionText = tr("reboot");
break;
}
setText(tr("A %1 was triggered by a remote workstation.").arg(actionText));
connect(m_ui->buttonBox->getButton(LtsWidgets::DialogButton::Ok), &QPushButton::clicked, this,
&LogoutInformationDialog::acceptClicked);
connect(m_ui->buttonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this,
&LogoutInformationDialog::closeClicked);
connect(m_timer, &QTimer::timeout, this, &LogoutInformationDialog::onTimerTimeout);
m_timer->setInterval(1000);
}
LogoutInformationDialog::~LogoutInformationDialog()
{
delete m_ui;
}
void LogoutInformationDialog::setText(const QString& text)
{
m_ui->lblLogoutText->setText(text);
}
void LogoutInformationDialog::onTimerTimeout()
{
if (m_secsLeft > 0)
{
m_secsLeft--;
setCloseButtonText(m_secsLeft);
}
else
{
acceptClicked();
}
}
void LogoutInformationDialog::acceptClicked()
{
m_timer->stop();
accept();
}
void LogoutInformationDialog::closeClicked()
{
m_timer->stop();
reject();
}
void LogoutInformationDialog::setCloseButtonText(int secsLeft)
{
m_ui->buttonBox->getButton(LtsWidgets::DialogButton::Close)->setText(tr("Abort [%1]").arg(secsLeft));
}
void LogoutInformationDialog::showEvent(QShowEvent* event)
{
m_secsLeft = m_timeOut;
setCloseButtonText(m_secsLeft);
m_timer->start();
LtsWidgets::LtsFramelessDialog::showEvent(event);
}
} // namespace LtsLogin
@@ -1,44 +0,0 @@
#pragma once
#include <AssetManagement/asm.hpp>
#include <LtsWidgets/lts_frameless_dialog.h>
#include <QtCore/QObject>
class QTimer;
namespace Ui
{
class LogoutInformationDialog;
}
namespace LtsLogin
{
class LogoutInformationDialog : public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
LogoutInformationDialog(int timeOut, AssetManagement::ShutdownAction shutdownAction, QWidget* parent = nullptr);
~LogoutInformationDialog();
private slots:
void onTimerTimeout();
void acceptClicked();
void closeClicked();
private:
void setText(const QString& text);
void setCloseButtonText(int secsLeft);
Ui::LogoutInformationDialog* m_ui;
QTimer* m_timer;
int m_timeOut;
int m_secsLeft;
protected:
void showEvent(QShowEvent* event) override final;
};
} // namespace LtsLogin
@@ -1,76 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogoutInformationDialog</class>
<widget class="QWidget" name="LogoutInformationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>165</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="lblLogoutText">
<property name="text">
<string notr="true">&lt;logout information text&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsDialogButtonBox" name="buttonBox"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsDialogButtonBox</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_dialog_button_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -1,282 +0,0 @@
#include "lts_login.h"
#include "logout_info_dialog.h"
#include <LtsAppDescriptor/lts_app_descriptor.h>
#include <LtsPluginManager/lts_plugin_manager.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsSession/lts_session_manager.h>
#include <LtsUtils/LtsUtilsCore/dds_vector.h>
#include <LtsUtilsSettings/settings.h>
#include <LtsWidgets/lts_wait_dialog.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtWidgets/QMessageBox>
#include <QtCore/QProcess>
namespace LtsLogin
{
const int logoutShutdownTimeout = 2 * 60;
LtsLogin::LtsLogin()
: m_loginApp(nullptr), m_shutdownAction(AssetManagement::ShutdownAction::NoAction), m_isInLogoutDialog(false)
{
}
LtsLogin::~LtsLogin()
{
delete m_loginApp;
m_loginApp = nullptr;
}
void LtsLogin::login()
{
LtsAppDescriptor::PluginInfo opmDataPlugin;
opmDataPlugin.iid = QStringLiteral("opm");
opmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA;
LtsAppDescriptor::PluginInfo pdmDataPlugin;
pdmDataPlugin.iid = QStringLiteral("pdm");
pdmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA;
LtsAppDescriptor::PluginInfo asmDataPlugin;
asmDataPlugin.iid = QStringLiteral("asm");
asmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA;
LtsAppDescriptor::PluginInfo srmDataPlugin;
srmDataPlugin.iid = QStringLiteral("srm");
srmDataPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA;
LtsAppDescriptor::PluginInfo guiPlugin;
guiPlugin.iid = m_loginPluginIid;
guiPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::GUI;
guiPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::PAYLOAD;
QVector<LtsAppDescriptor::PluginInfo> plugins;
plugins.append(opmDataPlugin);
plugins.append(pdmDataPlugin);
plugins.append(asmDataPlugin);
plugins.append(srmDataPlugin);
plugins.append(guiPlugin);
LtsAppDescriptor::AppDescription app;
app.active = true;
app.appName = QStringLiteral("Login");
app.pluginInfos = plugins;
LtsSession::DdsConnectionParams connParams;
connParams.domainId = LtsSessionData::LtsSessionData::getInstance()->getDomainIdPreset();
connParams.tcpConnectionUrl = LtsSessionData::LtsSessionData::getInstance()->getTcpConnectionUrl();
connParams.partitionType = IDL::PartitionType::Global;
connParams.partitionId = 0;
m_loginApp = new LtsPluginManager::LtsPluginManager();
bool result = m_loginApp->startApp(app, true, connParams);
if (!result)
{
qWarning() << "no login plugin - probably an offline project";
emit signalLoginDone();
return;
}
connect(m_loginApp, &LtsPluginManager::LtsPluginManager::signalInitializationFinished, this, [this]() {
disconnect(m_loginApp, &LtsPluginManager::LtsPluginManager::signalInitializationFinished, this, nullptr);
LtsSession::LtsSession* session = getAdminSession();
if (!session)
{
qCritical() << "I cannot find the Admin Session :( - abort!";
QMessageBox::critical(nullptr, tr("Error loading login app"), tr("Could not load login app - abort."));
abort();
}
if (session->isInitializationDone())
QMetaObject::invokeMethod(this, &LtsLogin::slotLoginDone);
else
connect(session, &LtsSession::LtsSession::signalDoneInitializingSession, this, &LtsLogin::slotLoginDone,
Qt::UniqueConnection);
});
}
void LtsLogin::slotLoginDone()
{
LtsSession::LtsSession* session = getAdminSession();
if (session != nullptr)
{
disconnect(session, &LtsSession::LtsSession::signalDoneInitializingSession, this, &LtsLogin::slotLoginDone);
if (session->getDtc<AssetManagement::DataInterface>(m_asmDtc))
{
m_asmDtc->enableWorkstationLogout();
connect(m_asmDtc.get(), &AssetManagement::Interface::Dtc::workstationLogoutChanged, this,
&LtsLogin::slotLogoutShutdown);
}
else
qWarning() << "LtsLogin: no asm dtc, no remote logout/shutdown!";
}
else
{
qCritical() << "LtsLogin: no admin session for login!!";
QMessageBox::critical(nullptr, tr("Login error"), tr("Could not finalize login - abort."));
abort();
}
emit signalLoginDone();
}
void LtsLogin::showLogoutInformationDialog(AssetManagement::ShutdownAction action)
{
LogoutInformationDialog* lid = new LogoutInformationDialog(logoutShutdownTimeout, action, nullptr);
connect(lid, &LogoutInformationDialog::accepted, this, [this, action]() {
m_shutdownAction = action;
m_isInLogoutDialog = false;
emit signalLogout();
});
connect(lid, &LogoutInformationDialog::rejected, this, [this]() { m_isInLogoutDialog = false; });
lid->show();
}
void LtsLogin::slotLogoutShutdown(const AssetManagement::WorkstationLogout& data, DataModification modType)
{
if (m_isInLogoutDialog)
return;
if (m_asmDtc == nullptr || modType != DataModification::eAdded)
return;
if (!m_asmDtc->isAssetEnabled())
m_asmDtc->enableAsset();
for (int id : data.ids)
{
if (QString::fromStdString(m_asmDtc->asset(id).name)
.compare(LtsSession::LtsSession::workstationName(), Qt::CaseInsensitive) == 0)
{
m_isInLogoutDialog = true;
showLogoutInformationDialog(data.shutdownAction);
break;
}
}
}
LtsSession::LtsSession* LtsLogin::getAdminSession()
{
return m_loginApp->m_sessionManager->getSession(IDL::PartitionType::Global, 0);
}
void LtsLogin::logoff()
{
if (m_loginApp == nullptr)
{
qCritical() << "LtsLogin::logoff: missing login app!";
return;
}
LtsSession::LtsSession* datSession = getAdminSession();
if (!datSession)
{
qCritical() << "LtsLogin::logoff: no admin session for logout!";
abort();
}
int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
int workstationId = LtsSessionData::LtsSessionData::getInstance()->getWorkstationId();
LtsSessionData::LtsSessionData::getInstance()->clear();
std::shared_ptr<OperatorManagement::Interface::Ftc> opmFtc;
if (operatorId == -1 || !datSession->getFtc<OperatorManagement::DataInterface>(opmFtc))
{
}
else
{
try
{
LtsUtils::OperatorSettings* os = LtsUtils::OperatorSettings::getInstance();
const QByteArray settings = os->exportSettings();
const auto vec(LtsUtils::convert<std::vector<uint8_t>>(settings));
os->clear();
opmFtc->operatorGetSetSettingsRequest(OperatorManagement::OperatorGetSetSettingsRequest(operatorId, vec));
}
catch (const std::runtime_error& err)
{
qCritical() << "Saving operator settings failed!" << err.what();
}
std::shared_ptr<SituationReplayManagement::ForeignDataInterface> srmIfc;
if (datSession->getForeignDataInterface<SituationReplayManagement::DataInterface>(srmIfc))
{
try
{
srmIfc->removeReplayPartitionsWorkstation(workstationId);
}
catch (const std::runtime_error& err)
{
qCritical() << "Removing reply partitions from this workstation failed!" << err.what();
}
try
{
srmIfc->removeReplayPartitionsOperator(operatorId);
}
catch (const std::runtime_error& err)
{
qCritical() << "Removing reply partitions from this operator failed!" << err.what();
}
}
try
{
opmFtc->operatorLogoutRequest(OperatorManagement::OperatorLogoutRequest(operatorId));
}
catch (const std::runtime_error& err)
{
qCritical() << "Logout failed!" << err.what();
}
}
m_loginApp->deleteLater();
m_loginApp = nullptr;
QString cmd;
switch (m_shutdownAction)
{
case AssetManagement::ShutdownAction::Shutdown:
#ifdef LTS_DEBUG
cmd = QStringLiteral("xeyes");
#else
cmd = QStringLiteral("systemctl poweroff");
#endif
break;
case AssetManagement::ShutdownAction::Reboot:
#ifdef LTS_DEBUG
cmd = QStringLiteral("kcalc");
#else
cmd = QStringLiteral("systemctl reboot");
#endif
break;
case AssetManagement::ShutdownAction::Logout:
case AssetManagement::ShutdownAction::NoAction:
emit signalLogoffDone();
break;
}
if (!cmd.isEmpty())
{
QProcess::startDetached(cmd);
QCoreApplication::quit();
}
}
} // namespace LtsLogin
@@ -1,71 +0,0 @@
#pragma once
#include <AssetManagement/asm_data_plugin_interface.h>
#include <OperatorManagement/opm_data_plugin_interface.h>
#include <SituationReplayManagement/srm_data_plugin_interface.h>
#include <QtCore/QObject>
#include <ltslogin_export.h>
namespace LtsPluginManager
{
class LtsPluginManager;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace LtsSession
{
class LtsSession;
}
namespace DeveloperTools
{
class DevDialog;
}
namespace LtsLogin
{
class LTSLOGIN_EXPORT LtsLogin : public QObject
{
Q_OBJECT
public:
LtsLogin();
~LtsLogin();
void login();
void logoff();
void setLoginPlugin(const QString& loginPluginIid)
{
m_loginPluginIid = loginPluginIid;
}
private:
friend class DeveloperTools::DevDialog;
LtsSession::LtsSession* getAdminSession();
void showLogoutInformationDialog(AssetManagement::ShutdownAction action);
LtsPluginManager::LtsPluginManager* m_loginApp;
AssetManagement::ShutdownAction m_shutdownAction;
std::shared_ptr<AssetManagement::Interface::Dtc> m_asmDtc;
QString m_loginPluginIid;
bool m_isInLogoutDialog;
private slots:
void slotLoginDone();
void slotLogoutShutdown(const AssetManagement::WorkstationLogout& data, DataModification modType);
signals:
void signalLogout();
void signalStopBlock();
void signalLoginDone();
void signalLogoffDone();
};
} // namespace LtsLogin
@@ -1,68 +0,0 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Created by Mike Wille, SLSE, 2016-11-01
#
# LtsMultiMonitor
# Make ${Qt6_Gui} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Gui)
set(Qt6_Gui ${Qt6_Gui})
else()
set(Qt6_Gui "")
endif()
# Make ${Qt6_Widgets} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets)
set(Qt6_Widgets ${Qt6_Widgets})
else()
set(Qt6_Widgets "")
endif()
SET(LtsMultiMonitor_SRCS
lts_multi_monitor_export.h
lts_multi_monitor.cpp
lts_dummy_widget.cpp
lts_screen_container.cpp
lts_multi_monitor_selector_base.cpp
lts_screen_base.cpp
lts_magic_corner.h
lts_magic_corner.cpp
# normal screen
lts_multi_monitor_selector_normal.cpp
lts_screen_normal.cpp
# ultra wide screen
lts_multi_monitor_selector_wide.cpp
lts_screen_wide.cpp
)
# LtsMultiMonitor is needed by AvmShared regardless of BUILD_RTI
NGL_ADD_LIBRARY2(WS LtsMultiMonitor SHARED ${LtsMultiMonitor_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsMultiMonitor
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_SOURCE_DIR}/Lts/Common/LtsUtils
)
if(QT6_GUI_AVAILABLE)
NGL_TARGET_LINK_LIBRARIES(LtsMultiMonitor
PUBLIC
Qt6::Core
${Qt6_Widgets}
PRIVATE
${Qt6_Gui}
LtsWidgets
LtsSessionData
LtsUtilsCore
LtsUtilsGuiDds
)
endif()
NGL_INSTALL_TARGET(LtsMultiMonitor)
@@ -1,35 +0,0 @@
#include "lts_dummy_widget.h"
#include <LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h>
#include <QtCore/QDebug>
#include <QtCore/QEvent>
#include <QtGui/QIcon>
namespace LtsMultiMonitor
{
FullscreenVideoDummyWidget::FullscreenVideoDummyWidget()
{
setWindowIcon(LtsUtils::iconFromSvg(QStringLiteral(":/style/mobile_video.svg")));
setWindowTitle(tr("Fullscreen Video Helper"));
}
void FullscreenVideoDummyWidget::hideVideo()
{
qWarning() << "FullscreenVideoDummyWidget::hideVideo()";
emit signalHideVideo();
}
void FullscreenVideoDummyWidget::showVideo()
{
emit signalShowVideo();
}
bool FullscreenVideoDummyWidget::event(QEvent* event)
{
if (event->type() == QEvent::Type::Show)
assert(false);
return QWidget::event(event);
}
} // namespace LtsMultiMonitor
@@ -1,39 +0,0 @@
#pragma once
#include "ltsmultimonitor_export.h"
#include <QtCore/QObject>
#include <QtWidgets/QWidget>
class QEvent;
namespace LtsMultiMonitor
{
class LTSMULTIMONITOR_EXPORT SecondaryWidget : public QWidget
{
Q_OBJECT
public:
SecondaryWidget() = default;
virtual ~SecondaryWidget() = default;
};
class LTSMULTIMONITOR_EXPORT FullscreenVideoDummyWidget : public SecondaryWidget
{
Q_OBJECT
public:
FullscreenVideoDummyWidget();
void hideVideo();
void showVideo();
protected:
bool event(QEvent*);
signals:
void signalWidgetPlaced(const QRect& globalGeometry);
void signalHideVideo();
void signalShowVideo();
};
} // namespace LtsMultiMonitor
@@ -1,130 +0,0 @@
#include "lts_magic_corner.h"
#include <LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h>
#include <QtCore/QDebug>
#include <QtWidgets/QHBoxLayout>
#include <QtGui/QIcon>
#include <QtWidgets/QLabel>
#include <QtGui/QMouseEvent>
#include <QtCore/QTimer>
#include <chrono>
#define SMOL 48
#define SWOLE 100
using namespace std::chrono_literals;
namespace LtsMultiMonitor
{
LtsMagicCorner::LtsMagicCorner(const QRect& neogeo)
: m_neogeo(neogeo), m_shrinkTimer(new QTimer(this)), m_label(new QLabel(this))
{
m_shrinkTimer->setInterval(1500ms);
m_shrinkTimer->setSingleShot(true);
connect(m_shrinkTimer, &QTimer::timeout, this, &LtsMagicCorner::makeSmol);
makeSmol();
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
setMouseTracking(true);
setContentsMargins(0, 0, 0, 0);
setWindowFlag(Qt::WindowType::WindowStaysOnTopHint);
setWindowFlag(Qt::WindowType::X11BypassWindowManagerHint);
activateWindow();
auto* t = new QTimer(this);
connect(t, &QTimer::timeout, this, [this]() {
if (!isVisible())
return;
raise();
});
t->setInterval(750ms);
t->start();
m_label->setMinimumSize(SWOLE, SWOLE);
m_label->setMaximumSize(SWOLE, SWOLE);
m_label->setGeometry(0, 0, SWOLE, SWOLE);
m_label->setText(QStringLiteral("<unknown>"));
m_label->setAlignment(Qt::AlignmentFlag::AlignCenter);
}
void LtsMagicCorner::setActiveWidgetType(LtsScreenBase::WidgetType widgetType)
{
m_activeWidgetType = widgetType;
if (widgetType == LtsScreenBase::WidgetType::MainWidget)
m_label->setPixmap(m_mainPixmap);
else
m_label->setPixmap(m_secondaryPixmap);
}
void LtsMagicCorner::setFreezed(bool freezed)
{
setVisible(!freezed);
}
void LtsMagicCorner::setIcon(const QIcon& icon, LtsScreenBase::WidgetType widgetType)
{
QPixmap p(LtsUtils::pixmapFromIcon(icon));
if (widgetType == LtsScreenBase::WidgetType::MainWidget)
m_mainPixmap = p;
else
m_secondaryPixmap = p;
if (widgetType != m_activeWidgetType)
m_label->setPixmap(p);
}
void LtsMagicCorner::mousePressEvent(QMouseEvent* event)
{
m_mousePressed = (event->button() == Qt::MouseButton::LeftButton);
}
void LtsMagicCorner::mouseReleaseEvent(QMouseEvent* event)
{
if (!m_mousePressed)
return;
m_mousePressed = false;
if (event->button() != Qt::MouseButton::LeftButton)
return;
m_shrinkTimer->start();
emit toggle();
}
void LtsMagicCorner::makeSmol()
{
setAttribute(Qt::WA_TranslucentBackground);
setGeometry(m_neogeo.x(), m_neogeo.y(), SMOL, SMOL);
setMinimumSize(SMOL, SMOL);
setMaximumSize(SMOL, SMOL);
m_label->hide();
}
void LtsMagicCorner::makeSwole()
{
if (!isVisible())
return;
setAttribute(Qt::WA_TranslucentBackground, false);
setAttribute(Qt::WA_NoSystemBackground, false);
setGeometry(m_neogeo.x(), m_neogeo.y(), SWOLE, SWOLE);
setMinimumSize(SWOLE, SWOLE);
setMaximumSize(SWOLE, SWOLE);
m_label->show();
m_shrinkTimer->start();
update();
}
void LtsMagicCorner::mouseMoveEvent(QMouseEvent*)
{
emit signalMakeAllSwole();
}
} // namespace LtsMultiMonitor
@@ -1,52 +0,0 @@
#pragma once
#include "lts_screen_base.h"
#include <QtWidgets/QDialog>
#include <QtCore/QObject>
#include <QtGui/QPixmap>
class QMouseEvent;
class QPaintEvent;
class QLabel;
class QTimer;
namespace LtsMultiMonitor
{
class LtsMagicCorner : public QDialog
{
Q_OBJECT
public:
explicit LtsMagicCorner(const QRect& neogeo);
void setIcon(const QIcon& icon, LtsScreenBase::WidgetType widgetType);
void setActiveWidgetType(LtsScreenBase::WidgetType widgetType);
void setFreezed(bool freezed);
protected:
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
signals:
void toggle();
void signalMakeAllSwole();
private slots:
void makeSmol();
public slots:
void makeSwole();
private:
QRect m_neogeo;
QTimer* m_shrinkTimer;
bool m_mousePressed{false};
bool m_freezed = false;
QLabel* m_label;
QPixmap m_mainPixmap;
QPixmap m_secondaryPixmap;
LtsScreenBase::WidgetType m_activeWidgetType;
};
} // namespace LtsMultiMonitor
@@ -1,602 +0,0 @@
#include "lts_multi_monitor.h"
#include "lts_dummy_widget.h"
#include "lts_multi_monitor_selector_normal.h"
#include "lts_multi_monitor_selector_wide.h"
#include "lts_screen_container.h"
#include "lts_screen_normal.h"
#include "lts_screen_wide.h"
#include <LtsSession/lts_session_data.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsWidgets/lts_message_dialog.h>
#include <QtCore/QDebug>
#include <QtGui/QGuiApplication>
#include <QtWidgets/QMessageBox>
#include <QtCore/QPointer>
#include <QtCore/QRect>
#include <QtGui/QScreen>
#include <QtWidgets/QWidget>
#include <utility>
#ifdef Q_OS_WIN
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
#endif
namespace LtsMultiMonitor
{
static LtsMultiMonitor* s_ltsMultiMonitor = nullptr;
LtsMultiMonitor* LtsMultiMonitor::getInstance()
{
if (!s_ltsMultiMonitor)
{
s_ltsMultiMonitor = new LtsMultiMonitor;
qAddPostRoutine(&cleanupLtsMultiMonitor);
}
return s_ltsMultiMonitor;
}
void LtsMultiMonitor::cleanupLtsMultiMonitor()
{
delete s_ltsMultiMonitor;
s_ltsMultiMonitor = nullptr;
}
LtsMultiMonitor::LtsMultiMonitor()
: QObject(nullptr), m_mainScreen(nullptr), m_active(false), m_initialized(false), m_skipScreenId(-1),
m_subSkipScreenId(-1)
,
m_overlapBaseLayouts(false), m_debugMargins(false)
{
}
LtsMultiMonitor::~LtsMultiMonitor()
{
for (auto i = m_screens.begin(); i != m_screens.end(); ++i)
{
LtsScreenBase* screen = *i;
disconnect(screen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied);
screen->deleteLater();
}
}
void LtsMultiMonitor::screenDied()
{
Q_ASSERT(false);
}
void LtsMultiMonitor::init()
{
if (LtsSessionData::LtsSessionData::getInstance()->isTablet())
{
m_overlapBaseLayouts = true;
m_initialized = true;
return;
}
if (m_initialized || !m_active)
return;
QList<QScreen*> screenList = QGuiApplication::screens();
const QScreen& mainScreen = *QGuiApplication::primaryScreen();
for (auto i = screenList.constBegin(); i != screenList.constEnd(); ++i)
{
const QScreen& screen = **i;
bool found = false;
for (LtsScreenBase* ltsScreen : std::as_const(m_screens))
{
if (ltsScreen->serialNumber() == screen.name())
{
found = true;
break;
}
}
if (found)
continue;
int screenId = m_nextScreenId++;
QRect screenGeo(screen.geometry());
if (screenId == m_skipScreenId)
{
if (m_subSkipScreenId == 0)
{
QRect newScreenGeo(screenGeo);
newScreenGeo.moveLeft(screenGeo.x() + (screenGeo.width() / 2));
newScreenGeo.setWidth(screenGeo.width() / 2);
screenGeo = newScreenGeo;
}
else if (m_subSkipScreenId == 1)
{
QRect newScreenGeo(screenGeo);
newScreenGeo.setWidth(screenGeo.width() / 2);
screenGeo = newScreenGeo;
}
else
{
continue;
}
}
bool isUltraWideView = (screenGeo.width() > 3000);
LtsScreenBase* newScreen;
if (isUltraWideView)
{
newScreen = new LtsScreenWide(screenGeo, screenId, m_debugMargins, screen.name());
}
else
{
auto* newScreenNormal = new LtsScreenNormal(std::move(screenGeo), screenId, m_debugMargins, screen.name());
newScreen = newScreenNormal;
connect(newScreenNormal, &LtsScreenNormal::signalMakeAllSwole, this, &LtsMultiMonitor::signalMakeAllSwole);
connect(this, &LtsMultiMonitor::signalMakeAllSwole, newScreenNormal, &LtsScreenNormal::signalMakeSwole);
}
connect(newScreen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied);
if ((&mainScreen) == (&screen))
m_mainScreen = newScreen;
m_screens.append(newScreen);
}
if (m_screens.isEmpty())
{
QMessageBox::critical(nullptr, QString(), tr("No screens found - cannot start application."));
abort();
}
if (m_mainScreen == nullptr)
m_mainScreen = m_screens.first();
m_initialized = true;
emit signalScreenCountChanged(screenCount());
}
int LtsMultiMonitor::screenCount() const
{
return m_screens.count();
}
bool LtsMultiMonitor::cancelAndProcessIfInactive(QWidget* widget) const
{
if (m_active)
return false;
widget->activateWindow();
widget->show();
place(widget);
return true;
}
void LtsMultiMonitor::place(QWidget* widget)
{
auto* blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget*>(widget);
if (blw != nullptr)
emit blw->signalSuccessfullyPlaced();
}
bool LtsMultiMonitor::putScreen(QWidget* widget, int screenId, int screenPart)
{
Q_ASSERT(widget != nullptr);
if (widget == nullptr)
return false;
if (cancelAndProcessIfInactive(widget))
return true;
init();
if (overlapBaseLayouts(widget))
return true;
bool isSecondary = isWidgetSecondary(widget);
qWarning() << "putScreen: isSecondary" << isSecondary;
for (LtsScreenBase* screen : std::as_const(m_screens))
{
if (screen->screenId() != screenId)
continue;
screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget
: LtsScreenBase::WidgetType::MainWidget);
try
{
screen->putWidget(widget, screenPart);
}
catch (...)
{
screen->revertSwitch();
return selectScreen(widget);
}
screen->check();
place(widget);
screen->finalizeSwitch();
return true;
}
return selectScreen(widget);
}
bool LtsMultiMonitor::putScreenAutomatically(QWidget* widget)
{
if (widget == nullptr)
return false;
if (cancelAndProcessIfInactive(widget))
return true;
init();
if (overlapBaseLayouts(widget))
return true;
for (LtsScreenBase* screen : std::as_const(m_screens))
{
if (screen->screenId() == m_skipScreenId || !screen->isEmpty())
continue;
screen->switchToTemp(LtsScreenBase::WidgetType::SecondaryWidget);
try
{
screen->putWidget(widget, 1);
}
catch (...)
{
try
{
screen->putWidget(widget, 2);
}
catch (...)
{
screen->revertSwitch();
continue;
}
}
screen->check();
place(widget);
screen->finalizeSwitch();
return true;
}
return false;
}
bool LtsMultiMonitor::isOverlapBaseLayouts() const
{
return (m_overlapBaseLayouts);
}
void LtsMultiMonitor::freezeMagicCorner(int screenId)
{
for (auto* screen : std::as_const(m_screens))
{
if (screen->screenId() == screenId)
{
LtsScreenNormal* normalScreen = qobject_cast<LtsScreenNormal*>(screen);
if (normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget())
{
normalScreen->hideMagicCorner(true);
if (normalScreen->child()->activeWidgetType() == LtsScreenBase::WidgetType::MainWidget)
normalScreen->child()->toggle();
}
}
}
}
void LtsMultiMonitor::unfreezeMagicCorner(int screenId)
{
for (auto* screen : std::as_const(m_screens))
{
if (screen->screenId() == screenId)
{
LtsScreenNormal* normalScreen = qobject_cast<LtsScreenNormal*>(screen);
if (normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget())
normalScreen->hideMagicCorner(false);
}
}
}
bool LtsMultiMonitor::overlapBaseLayouts(QWidget* widget)
{
if (!isOverlapBaseLayouts())
return false;
Q_ASSERT(qobject_cast<FullscreenVideoDummyWidget*>(widget) == nullptr);
if (qobject_cast<FullscreenVideoDummyWidget*>(widget) != nullptr)
{
return false;
}
#ifdef Q_OS_WIN
widget->winId();
QWindowsWindowFunctions::setHasBorderInFullScreen(widget->windowHandle(), true);
#endif
widget->activateWindow();
widget->showFullScreen();
place(widget);
return true;
}
bool LtsMultiMonitor::selectAndSwap(QWidget* widget)
{
if (!m_active)
return false;
Q_ASSERT(!isOverlapBaseLayouts());
if (isOverlapBaseLayouts())
return false;
LtsScreenContainer* oldContainer = nullptr;
for (LtsScreenBase* _screen : std::as_const(m_screens))
{
try
{
oldContainer = _screen->containerOfWidget(widget);
}
catch (...)
{
}
}
if (oldContainer == nullptr)
{
LtsWidgets::LtsMessageDialog::showMessage(
widget,
tr("Could not determine screen id for this swap!<br>Swapping not possible, please try again later."));
return false;
}
return selectScreen(widget, oldContainer);
}
LtsScreenBase* LtsMultiMonitor::widgetsScreen(QWidget* widget)
{
for (LtsScreenBase* screen : std::as_const(m_screens))
if (screen->hasWidget(widget))
return screen;
return nullptr;
}
bool LtsMultiMonitor::isWidgetSecondary(QWidget* widget)
{
#ifndef GUEZ_MODE
return false;
#endif
auto* blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget*>(widget);
auto* secondaryWidget = qobject_cast<SecondaryWidget*>(widget);
bool isSecondaryWidget = ((blw != nullptr && blw->isSecondaryWidget()) || secondaryWidget != nullptr);
return isSecondaryWidget;
}
class StupidDeleter
{
public:
explicit StupidDeleter(LtsScreenBase* screen) : m_screen(screen) {}
~StupidDeleter()
{
if (m_screen != nullptr)
{
m_screen->revertSwitch();
}
}
void reset()
{
m_screen = nullptr;
}
private:
LtsScreenBase* m_screen;
};
bool LtsMultiMonitor::selectScreen(QWidget* widget, LtsScreenContainer* swap, bool putOnMainIfFree,
bool ltsScreenNormalOnly, const QVector<QWidget*>& irreplacableWidgets)
{
Q_ASSERT(widget != nullptr);
if (widget == nullptr)
return false;
if (cancelAndProcessIfInactive(widget))
return true;
init();
if (overlapBaseLayouts(widget))
return true;
bool isSecondary = isWidgetSecondary(widget);
if (!isSecondary)
{
bool allEmpty = putOnMainIfFree;
for (LtsScreenBase* screen : std::as_const(m_screens))
{
screen->switchToTemp(LtsScreenBase::WidgetType::MainWidget);
bool isEmpty = screen->isEmpty();
screen->revertSwitch();
if (isEmpty)
continue;
allEmpty = false;
break;
}
if (allEmpty)
{
m_mainScreen->switchToTemp(LtsScreenBase::WidgetType::MainWidget);
try
{
m_mainScreen->putWidget(widget);
}
catch (...)
{
qWarning() << "failed - reverting";
m_mainScreen->revertSwitch();
Q_ASSERT_X(false, __FUNCTION__,
"screen promised to be empty but 'putWidgetSomewhere' method failed regardless");
auto* blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget*>(widget);
if (blw != nullptr)
emit blw->signalClose();
else
widget->deleteLater();
return false;
}
m_mainScreen->check();
place(widget);
m_mainScreen->finalizeSwitch();
return true;
}
}
auto* selectionProcessWatcher = new QObject;
bool minOnce = false;
for (auto i = m_screens.begin(); i != m_screens.end(); ++i)
{
LtsScreenBase* screen = *i;
screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget
: LtsScreenBase::WidgetType::MainWidget);
StupidDeleter _(screen);
if (screen->isIrreplaceable())
{
continue;
}
bool skip = false;
for (QWidget* widget : std::as_const(irreplacableWidgets))
{
if (screen->hasWidget(widget))
{
skip = true;
continue;
}
}
if (skip)
continue;
MultiMonitorSelectorBase* mms;
if (qobject_cast<LtsScreenNormal*>(screen) != nullptr)
{
auto* screenNormal = static_cast<LtsScreenNormal*>(screen);
mms = new MultiMonitorSelectorNormal(screenNormal, isSecondary, irreplacableWidgets);
}
else if (qobject_cast<LtsScreenWide*>(screen) != nullptr && !ltsScreenNormalOnly)
{
auto* screenWide = static_cast<LtsScreenWide*>(screen);
mms = new MultiMonitorSelectorWide(screenWide, nullptr, irreplacableWidgets);
}
else
continue;
QPointer<QWidget> widgetPtr(widget);
connect(widget, &QWidget::destroyed, mms, &MultiMonitorSelectorBase::close);
connect(mms, &MultiMonitorSelectorBase::rejected, this, [widgetPtr, swap]() {
if (!widgetPtr || swap != nullptr)
return;
auto* blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget*>(widgetPtr.data());
if (blw != nullptr)
emit blw->signalClose();
else
widgetPtr->deleteLater();
});
connect(mms, &MultiMonitorSelectorBase::signalSelected, this, [this, mms, widgetPtr]() {
for (LtsScreenBase* screen : std::as_const(m_screens))
screen->check();
if (widgetPtr)
place(widgetPtr.data());
mms->deleteLater();
emit signalSelected();
});
connect(mms, &MultiMonitorSelectorBase::destroyed, selectionProcessWatcher, &QObject::deleteLater);
connect(selectionProcessWatcher, &QObject::destroyed, mms, &MultiMonitorSelectorBase::close);
mms->setSwap(swap);
mms->setWidget(widget);
mms->show();
_.reset();
minOnce = true;
}
if (!minOnce)
{
selectionProcessWatcher->deleteLater();
auto* blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget*>(widget);
if (blw != nullptr)
emit blw->signalClose();
else
widget->deleteLater();
LtsWidgets::LtsMessageDialog::showMessage(m_mainScreen->screenDialog(),
tr("There are no free screens available to select from."));
return false;
}
return true;
}
} // namespace LtsMultiMonitor
@@ -1,95 +0,0 @@
#pragma once
#include "ltsmultimonitor_export.h"
#include <QtCore/QObject>
#include <QtCore/QVector>
#include <memory>
namespace LtsMultiMonitor
{
class LtsScreenBase;
class MultiMonitorSelectorBase;
class LtsScreenContainer;
class LTSMULTIMONITOR_EXPORT LtsMultiMonitor : public QObject
{
Q_OBJECT
public:
static LtsMultiMonitor* getInstance();
bool selectScreen(QWidget* widget, LtsScreenContainer* swap = nullptr, bool putOnMainIfFree = true,
bool ltsScreenNormalOnly = false, const QVector<QWidget*>& irreplacableWidgets = {});
bool selectAndSwap(QWidget* widget);
void setEnableMultiMonitorMode()
{
m_active = true;
}
void setDebugMultiMonitorMode()
{
m_debugMargins = true;
}
bool isActive() const
{
return m_active;
}
LtsScreenBase* widgetsScreen(QWidget* widget);
void freezeMagicCorner(int screenId);
void unfreezeMagicCorner(int screenId);
bool putScreen(QWidget* widget, int screenId, int screenPart);
bool putScreenAutomatically(QWidget* widget);
static void place(QWidget* widget);
void setSkipScreen(int screenId, int subScreenId = -1)
{
m_skipScreenId = screenId;
m_subSkipScreenId = subScreenId;
}
LtsScreenBase* mainScreen() const
{
return m_mainScreen;
}
int screenCount() const;
private slots:
void screenDied();
private:
LtsMultiMonitor();
~LtsMultiMonitor();
static void cleanupLtsMultiMonitor();
void init();
bool cancelAndProcessIfInactive(QWidget* widget) const;
static bool isWidgetSecondary(QWidget* widget);
bool overlapBaseLayouts(QWidget* widget);
bool isOverlapBaseLayouts() const;
QVector<LtsScreenBase*> m_screens;
int m_nextScreenId{0};
LtsScreenBase* m_mainScreen;
bool m_active;
bool m_initialized;
int m_skipScreenId;
int m_subSkipScreenId;
bool m_overlapBaseLayouts;
bool m_debugMargins;
signals:
void signalQuitLoop();
void signalSelected();
void signalScreenCountChanged(int);
void signalMakeAllSwole();
};
} // namespace LtsMultiMonitor
Loaded 100 of 765 files, more files were not shown because too many files have changed in this diff. Show more