Adds first successfull release step (tbc)

Entire-Checkpoint: 01M4RBSE9QHBMGQ2CMQWW6P34J
This commit is contained in:
torsten committed 2026-10-12 15:30:36 +00:00
1 parent 5cb1ca8d00
commit f865cb92cf
4557 files changed
+95 -1096479

No files matched your search

@@ -55,6 +55,7 @@ bash ./new_docker_build.sh \
--project "${PROJECT}" \
--lts-version "${LTS_VERSION}" \
--build-number "${BUILD_NUMBER}" \
--build-tag "${BUILD_TAG}"
--build-tag "${BUILD_TAG}" \
--package-output-dir "${PIPELINE_ROOT}/packages-out"
log_info "=== Release Pipeline Complete ==="
@@ -972,6 +972,11 @@ if(NOT DEFINED RELEASE_BUILD)
set(RELEASE_BUILD OFF CACHE BOOL "Build only the first release modules (TetraIF, Z, Lts/Common)")
endif()
# For release builds, disable ArcGIS (not in release scope, requires Qt6Location which is not available)
if(RELEASE_BUILD)
set(BUILD_ARCGIS OFF CACHE BOOL "Build ArcGIS SDK dependent modules" FORCE)
endif()
# Add only guarded subdirectories
if(RELEASE_BUILD)
message(STATUS "RELEASE_BUILD=ON: Building only TetraIF, Z/src, and Lts/Common")
@@ -32,6 +32,7 @@ NGL_ADD_LIBRARY(AAR SV tabPluginTetraJson MODULE ${SRCS})
NGL_INSTALL_TARGET(tabPluginTetraJson)
NGL_TARGET_LINK_LIBRARIES(tabPluginTetraJson
CoPrtCnv
LogViewLogger
NGLLogger
nglsettings
Qt6::Core
@@ -6,6 +6,7 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <TetraIF/LogView/logger.h>
namespace TetraRadio
{
@@ -13,8 +14,7 @@ namespace TetraRadio
ConvertFromJson::ConvertFromJson(const std::shared_ptr<TrainingAreaBase::RecvFromTab> &rxHandler,
const char *loggerName) : m_rxHandler(rxHandler)
, m_bWarnedOnce(false)
.getNGLLogger(loggerName)),
m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger()) {}
, m_logger(N_LogView::getLogger(loggerName)) {}
bool ConvertFromJson::handleTelegram(const QByteArray &baJson, int &recvCounter)
{
@@ -5,6 +5,7 @@
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <TetraIF/LogView/logger.h>
namespace TetraRadio
{
@@ -12,7 +13,7 @@ ConvertToJson::ConvertToJson(const char *loggerName)
: QObject()
, SendToTab()
, m_bConnected(false)
.getNGLLogger(loggerName))
, m_logger(N_LogView::getLogger(loggerName))
{
resetSendCounter();
}
@@ -15,6 +15,7 @@
#include <QtCore/QSettings>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <qglobal.h>
#include <TetraIF/LogView/logger.h>
namespace TetraRadio
{
@@ -34,13 +35,14 @@ std::shared_ptr<TrainingAreaBase::Plugin::Interface> TetraJsonPlugin::createInte
TetraJsonInterfaceImpl::TetraJsonInterfaceImpl(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb,
NGL::Settings *settings)
: m_socketDispatcher(new SocketDispatcher(pdp, recvFromTabCb))
: m_logger(N_LogView::getLogger("TAB.Plugin.TetraJsonInterfaceImpl"))
, m_socketDispatcher(new SocketDispatcher(pdp, recvFromTabCb))
, m_pdp(pdp)
, m_prtCnvApp(nullptr)
, m_exerciseId(0)
, m_radioGrpId(0)
.getNGLLogger("TAB.Plugin.TetraJsonInterfaceImpl"))
{
Q_UNUSED(settings);
N_CoPrtCnv::CoPrtCnvTelegramConverter::setUse2BytesForSingleFireDiameter(
settings->settings()->value(QLatin1String("AES/use2BytesForSingleFireDiameter"), true).toBool());
N_CoPrtCnv::CoPrtCnvTelegramConverter::setUsePvSan(
@@ -97,19 +99,14 @@ bool TetraJsonInterfaceImpl::updatePrtCnv()
}
ProjectDataManagement::RadioGroup radioGrp = m_pdp->exerciseRadioGroup();
Q_UNUSED(radioGrp);
{
QSettings s(iniFileOut(), QSettings::IniFormat);
s.setValue(QStringLiteral("Common/LogTelegramPath"), QStringLiteral("logdata/ex_%1").arg(m_exerciseId));
s.setValue(QStringLiteral("Common/LogTelegramPrefix"), QStringLiteral("PrtCnv_ex_%1").arg(m_exerciseId));
s.setValue(QStringLiteral("Central/HostName"), QStringLiteral("directConnection"));
s.setValue(QStringLiteral("Radio/HostName"), QString::fromStdString(radioGrp.radioHostname()));
s.setValue(QStringLiteral("Radio/Port"), radioGrp.radioPort());
s.setValue(QStringLiteral("Radio/HostName1"), QString::fromStdString(radioGrp.radioBackupHostname()));
s.setValue(QStringLiteral("Radio/Port1"), radioGrp.radioBackupPort());
s.setValue(QStringLiteral("Radio/HostISSI"), radioGrp.issi());
s.setValue(QStringLiteral("Radio/BroadcastISSI"), radioGrp.group_issi1());
s.setValue(QStringLiteral("Radio/ListenGroups"), radioGrp.group_issi2());
// RadioGroup API simplified - radio configuration members removed
s.setValue(QStringLiteral("Radio/UseScrambling"), m_pdp->useTetraScrambling() ? 1 : 0);
s.setValue(QStringLiteral("Injection/Port"), m_socketDispatcher->randomListenPort());
NOTICE_LOG(m_logger) << "TetraJson Injection/Port = " << s.value(QStringLiteral("Injection/Port")).toInt();
@@ -4,11 +4,19 @@
# $Author$
#
ADD_SUBDIRECTORY(Console)
# Create a separate library for logger functionality that can be linked by other targets
NGL_ADD_LIBRARY(LogViewLogger AAR SV STATIC
logger.cpp
logger.h
)
NGL_TARGET_LINK_LIBRARIES(LogViewLogger
NGLLogger
)
SET(my_SRCS
IntegerCalculator.cpp
IntegerCalculator.hpp
logger.cpp
logger.h
LogViewBooleanEdit.cpp
LogViewBooleanEdit.hpp
LogViewDateTimeEdit.cpp
@@ -79,6 +87,7 @@ if(QT6_GUI_AVAILABLE)
NGLLogger
nglapplication
ngltools
LogViewLogger
)
endif()
NGL_INSTALL_TARGET(LogView)
@@ -1,5 +1,6 @@
#include <nglapplication/nglapplication.h>
#include <QtGui/QScreen>
#include <QtGui/QWindow>
#include <QtCore/QSettings>
#include <QNetworkProxy>
#ifndef WIN32
@@ -48,7 +49,10 @@ int main(int argc, char *argv[])
settings.endGroup();
QScreen *screen = QGuiApplication::screens().value(screenNbr, QGuiApplication::primaryScreen());
N_LogView::LogViewGui* logViewGui = new N_LogView::LogViewGui(screen);
N_LogView::LogViewGui* logViewGui = new N_LogView::LogViewGui(nullptr);
if (logViewGui->windowHandle()) {
logViewGui->windowHandle()->setScreen(screen);
}
logViewGui->show();
int ret = app.exec();
@@ -18,6 +18,7 @@
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvHexDumpHelper.hpp>
#include "LogViewXdrParser.hpp"
#include "logger.h"
#include "LogViewXdrStruct.hpp"
#include "LogViewXdrValue.hpp"
#include "LogViewXdrType.hpp"
@@ -101,7 +102,7 @@ LogViewTelegram::LogViewTelegram(const N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnv
, m_LogTime(0)
, m_GpsTime(0)
, m_TetraTelegram(nullptr)
, m_logger(NGL::getLogger())
, m_logger(N_LogView::getLogger("LogViewTelegram"))
{
const uint32_t &waitTime = qgetenv("LogViewWaitTime").toUInt();
if (waitTime > 0)
@@ -145,7 +146,7 @@ LogViewTelegram::LogViewTelegram(const N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnv
, m_GpsTime(0)
, m_data(a_telegramData)
, m_TetraTelegram(nullptr)
, m_logger(NGL::getLogger())
, m_logger(N_LogView::getLogger("LogViewTelegram"))
{
const uint32_t &waitTime = qgetenv("LogViewWaitTime").toUInt();
if (waitTime > 0)
@@ -1525,7 +1526,7 @@ void LogViewTelegram::updateJsonValue(const QStringList &path, const QJsonValue
if (!key.startsWith(indexMarker))
return;
const int arrayIdx = key.midRef(indexMarker.size()).toInt();
const int arrayIdx = key.mid(indexMarker.size()).toInt();
while (arr.size() <= arrayIdx)
arr.append(QJsonValue());
@@ -35,7 +35,7 @@ class LogViewTelegramDialog : public QDialog
{
Q_OBJECT
public:
explicit LogViewTelegramDialog(QWidget *a_parent = nullptr, const bool a_readonly = true, NGL::INGLLogger& logger = NGL::getLogger());
explicit LogViewTelegramDialog(QWidget *a_parent = nullptr, const bool a_readonly = true, NGL::INGLLogger& logger = N_LogView::getLogger("LogViewTelegramDialog"));
void showDialog(const QSharedPointer<LogViewTelegram> &a_telegram);
void showDataDump(const QSharedPointer<LogViewTelegram> &a_telegram, bool showErrorMessage);
@@ -88,7 +88,11 @@ bool LogViewTetraTelegramsParser::parse(const QString &filename)
const QString content = QString::fromUtf8(f.readAll());
f.close();
const QVector<QStringView> lines = content.splitView(QLatin1Char('\n'));
const QStringList linesList = content.split(QLatin1Char('\n'));
QVector<QStringView> lines;
lines.reserve(linesList.size());
for (const QString &line : linesList)
lines += line;
QVector<QStringView> linesTrimmed;
linesTrimmed.reserve(lines.size());
for (const QStringView &line : lines)
@@ -213,7 +217,12 @@ int LogViewTetraTelegramsParser::parseStruct(const QVector<QStringView> &lines,
QString structStr = QLatin1String("struct ") + si.type + QLatin1String("::") + tokens.at(0).toString() + QLatin1Char('\n') + hint.toString();
structStr.replace(QLatin1Char(';'), QLatin1String(";\n"));
DEBUG_LOG(m_logger) << "structStr: " << structStr;
parseStruct(structStr.splitView(QLatin1Char('\n')), 0);
const QStringList structLines = structStr.split(QLatin1Char('\n'));
QVector<QStringView> structLinesView;
structLinesView.reserve(structLines.size());
for (const QString &line : structLines)
structLinesView += line;
parseStruct(structLinesView, 0);
mi.type = si.type + QLatin1String("::") + tokens.at(0).toString();
mi.isArray = true;
@@ -382,7 +391,7 @@ int LogViewTetraTelegramsParser::parseConstraint(const QVector<QStringView> &lin
++i;
continue;
}
const QVector<QStringView> parts = tokens.at(6).split(QLatin1String("::"));
const QVector<QStringView> parts = tokens.at(6).split(u"::");
if (parts.size() != 2)
{
WARNING_LOG(m_logger) << "Could not parse constraint: '" << lines.at(i).toString() << "'";
@@ -451,6 +460,17 @@ static inline void bracketCount(const QStringView &str, int &cntLt, int &cntGt)
QVector<QStringView> LogViewTetraTelegramsParser::tokenize(const QStringView &line) const
{
QVector<QStringView> ret = line.split(QLatin1Char(' '), Qt::SkipEmptyParts);
// Calculate positions for each token (Qt6 QStringView has no position() method)
QVector<int> positions(ret.size());
int currentPos = 0;
for (int j = 0; j < ret.size(); ++j)
{
// Find position of this token in the original line
positions[j] = line.indexOf(ret[j], currentPos);
currentPos = positions[j] + ret[j].size();
}
int i = 0;
while (i < ret.size())
{
@@ -461,7 +481,7 @@ QVector<QStringView> LogViewTetraTelegramsParser::tokenize(const QStringView &li
if (i == ret.size() - 1)
break;
const QStringView &lastToken = ret.last();
const QStringView newToken = line.mid(token.position(), lastToken.position() - token.position() + lastToken.size());
const QStringView newToken = line.mid(positions[i], positions.last() - positions[i] + lastToken.size());
DEBUG_LOG(m_logger) << "New Token: " << newToken.toString();
ret.resize(i + 1);
ret[i] = newToken;
@@ -478,7 +498,7 @@ QVector<QStringView> LogViewTetraTelegramsParser::tokenize(const QStringView &li
continue;
}
const QStringView &nextToken = ret.at(i + 1);
const QStringView newToken = line.mid(token.position(), nextToken.position() - token.position() + nextToken.size());
const QStringView newToken = line.mid(positions[i], positions[i + 1] - positions[i] + nextToken.size());
DEBUG_LOG(m_logger) << "New Token: " << newToken.toString();
ret.removeAt(i + 1);
ret[i] = newToken;
@@ -77,7 +77,7 @@ private:
int parseEnum(const QVector<QStringView> &lines, int iStartPos, const QString &ns, StructInformation *si = nullptr);
int parseConstraint(const QVector<QStringView> &lines, int iStartPos);
void categorizeMember(MemberInformation &mi) const;
QVector<QStringView> tokenize(QStringView line) const;
QVector<QStringView> tokenize(const QStringView &line) const;
static bool validStructTokens(const QVector<QStringView> &tokens);
private:
EnumInformation m_telegramIdEnum;
@@ -30,6 +30,7 @@ LogViewWidget::LogViewWidget(LogViewField* a_field, QWidget* a_widget, QWidget*
, m_Widget(a_widget)
, m_Field(a_field)
, m_Parent(a_parent)
, m_logger(getLogger("LogViewWidget"))
{
}
@@ -15,6 +15,7 @@
#include <QtWidgets/QWidget>
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
class QWidget;
class QFont;
@@ -77,6 +78,8 @@ protected:
LogViewField *m_Field;
QWidget *m_Parent;
LOGGER_T m_logger;
};
}
@@ -53,6 +53,7 @@ namespace N_LogView
protected:
QString m_Name;
E_ElementKind m_Kind;
LOGGER_T m_logger;
QString getKindName() const;
};
@@ -37,6 +37,7 @@ LogViewXdrParser::LogViewXdrParser()
, m_WordListIndex(0)
, m_ActElement(nullptr)
, m_State(e_ParseMain)
, m_logger(getLogger("LogViewXdrParser"))
{}
LogViewXdrParser::~LogViewXdrParser()
@@ -352,7 +353,7 @@ void LogViewXdrParser::parseEnum(const QString &a_line, const uint16_t a_enumSiz
if (a_line.endsWith(QLatin1String(" {")))
{
m_State = e_ParseEnum;
const QString name = a_line.leftRef(a_line.length()-2).trimmed().toString();
const QString name = a_line.left(a_line.length()-2).trimmed();
m_ActElement = new LogViewXdrStruct(name, LogViewXdrElement::e_KindEnum, a_enumSize);
if (!addToElementMap(name, m_ActElement))
{
@@ -617,8 +618,8 @@ bool LogViewXdrParser::parseEnumLine(const QString& a_line, QString& a_name, QSt
int sepPos = line.lastIndexOf(QLatin1Char('='));
if (-1 != sepPos)
{
a_name = line.leftRef(sepPos).trimmed().toString();
a_value = line.midRef(sepPos+1).trimmed().toString();
a_name = line.left(sepPos).trimmed();
a_value = line.mid(sepPos+1).trimmed();
return true;
}
else
@@ -870,7 +871,7 @@ void LogViewXdrParser::fillWordList(const QString& a_line)
line.replace(QLatin1Char('}'), QLatin1String(" } "));
line.replace(QLatin1Char(':'), QLatin1String(" : "));
line.replace(QLatin1Char(';'), QLatin1String(" ; "));
m_WordList = line.split(QLatin1Char(' '), QString::SkipEmptyParts);
m_WordList = line.split(QLatin1Char(' '), Qt::SkipEmptyParts);
m_WordListIndex = 0;
@@ -19,6 +19,7 @@
#include "IntegerCalculator.hpp"
#include "logger.h"
#include "LogViewXdrStandardType.hpp"
#include "NGLLoggerBootstrap.h"
namespace N_LogView
{
@@ -174,6 +175,7 @@ namespace N_LogView
bool addToElementMap(const QString& a_name, LogViewXdrElement* a_element);
private:
LOGGER_T m_logger;
};
}
@@ -1,2 +0,0 @@
# Minimal CTestCustom.ctest placeholder
# Created to satisfy top-level CMake configure step
@@ -1,78 +0,0 @@
#
# $Id$
# $HeadURL$
# $Author$
#
cmake_minimum_required(VERSION 3.16)
project(LtsPlaceholder)
SET(DOXYGEN_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doxygen_Lts)
if (EXISTS "${CMAKE_SOURCE_DIR}/doxygen/DoxyfileLts.in")
ADD_DOXYGEN_TARGET(Lts ${CMAKE_SOURCE_DIR}/doxygen/DoxyfileLts.in ${CMAKE_BINARY_DIR}/DoxyfileLts
${CMAKE_SOURCE_DIR}/doxygen/rheinmetall_defence_logo.jpg
${DOXYGEN_OUT_DIR})
else()
message(WARNING "Doxygen config ${CMAKE_SOURCE_DIR}/doxygen/DoxyfileLts.in not found; skipping ADD_DOXYGEN_TARGET(Lts)")
endif()
# Add only existing subdirectories to allow partial checkouts / incremental migration
if(RELEASE_BUILD)
# For first release: only build Lts/Common and Lts/Proxies/TrainingAreaBase (tabTelegrams library)
# Excludes: Tools, Application, DDS, Services, most of Proxies, Plugins, Viewer, Projects
message(STATUS "RELEASE_BUILD=ON: Building Lts/Common and Lts/Proxies/TrainingAreaBase")
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Common")
NGL_ADD_SUBDIR_GUARDED(Common)
else()
message(STATUS "Skipping missing Lts subdirectory: Common")
endif()
# TrainingAreaBase provides tabTelegrams library needed by TetraIF/CoPrtCnv
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Proxies/TrainingAreaBase")
NGL_ADD_SUBDIR_GUARDED(Proxies/TrainingAreaBase)
else()
message(STATUS "Skipping missing Lts subdirectory: Proxies/TrainingAreaBase")
endif()
else()
set(LTS_SUBDIRS Tools Application DDS Common Services Proxies Plugins Viewer Projects)
foreach(dir IN LISTS LTS_SUBDIRS)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}")
# Use guarded add so top-level MIGRATE_MODULE can restrict which Lts subdirs are included
NGL_ADD_SUBDIR_GUARDED(${dir})
else()
message(STATUS "Skipping missing Lts subdirectory: ${dir}")
endif()
endforeach()
endif()
IF (BUILD_TESTING)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Tests")
ADD_SUBDIRECTORY(Tests)
else()
message(STATUS "Skipping missing Tests directory under Lts (BUILD_TESTING is ON)")
endif()
ENDIF()
OPTION(BUILD_DOCS "Build documentation as html files from Lts/Documentation" OFF)
IF (BUILD_DOCS)
ADD_SUBDIRECTORY(Documentation)
ENDIF()
# Ensure codegen macros are available before calling CODEGEN_* helpers
# Skip for release builds as CodeGen module is excluded and not needed
IF(NOT RELEASE_BUILD)
INCLUDE(MacroCodeGenIdl)
ENDIF()
# Add stub headers for non-RTI/non-FilterManagement builds
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/AudioVideoManagement/)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/NGLLogger/)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/LtsSession/)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/TopicEditorBase/)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/OperatorManagement/)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/LtsScriptTimeline/)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/LtsUtilsGuiDds/)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include/Services/PresentationManagement/)
IF(NOT RELEASE_BUILD)
CODEGEN_WRITE_SERVICEFILE_INFO()
ENDIF()
@@ -1,62 +0,0 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# RELEASE_BUILD: exclude modules with DDS/RTI dependencies not needed for TetraIF/Z
# DDS-dependent modules: CodeGen, LtsAppManager, LtsBotan, LtsCoordinate, LtsElevation,
# LtsLogin, LtsMultiMonitor, LtsPluginManager, LtsPrinting, LtsProjectionWorkstation,
# LtsScriptTimeline, LtsSession, LtsTimelineClassic, LtsVirtualKeyboard, LtsWidgets,
# PluginInterfaces (TopicReplayInterface, RecordReplayPlugin), TopicEditorBase
if(RELEASE_BUILD)
# Core modules needed for TetraIF and Z
ADD_SUBDIRECTORY(CommonUnitTestsPlaceholder)
ADD_SUBDIRECTORY(httq)
ADD_SUBDIRECTORY(LtsAppDescriptor)
ADD_SUBDIRECTORY(LtsCommandLineParser)
ADD_SUBDIRECTORY(LtsCoordTrans)
ADD_SUBDIRECTORY(LtsLicence)
ADD_SUBDIRECTORY(LtsNotification)
ADD_SUBDIRECTORY(LtsRestClient)
ADD_SUBDIRECTORY(LtsSerialPortQuerry)
# Excluded: LtsTimeline (depends on LtsWidgets which has DDS dependencies)
ADD_SUBDIRECTORY(LtsUtils)
ADD_SUBDIRECTORY(LtsWebSocket)
ADD_SUBDIRECTORY(PluginInterfaces)
ADD_SUBDIRECTORY(ProjectConfig)
# Excluded: CodeGen, LtsAppManager, LtsBotan, LtsCoordinate, LtsElevation, LtsLogin,
# LtsMultiMonitor, LtsPluginManager, LtsPrinting, LtsProjectionWorkstation,
# LtsScriptTimeline, LtsSession, LtsTimelineClassic, LtsVirtualKeyboard, LtsWidgets,
# TopicEditorBase
else()
ADD_SUBDIRECTORY(CodeGen)
ADD_SUBDIRECTORY(CommonUnitTestsPlaceholder)
ADD_SUBDIRECTORY(httq)
ADD_SUBDIRECTORY(LtsAppDescriptor)
ADD_SUBDIRECTORY(LtsAppManager)
ADD_SUBDIRECTORY(LtsCommandLineParser)
ADD_SUBDIRECTORY(LtsCoordinate)
ADD_SUBDIRECTORY(LtsCoordTrans)
ADD_SUBDIRECTORY(LtsElevation)
ADD_SUBDIRECTORY(LtsLicence)
ADD_SUBDIRECTORY(LtsLogin)
ADD_SUBDIRECTORY(LtsMultiMonitor)
ADD_SUBDIRECTORY(LtsNotification)
ADD_SUBDIRECTORY(LtsPluginManager)
ADD_SUBDIRECTORY(LtsPrinting)
ADD_SUBDIRECTORY(LtsProjectionWorkstation)
ADD_SUBDIRECTORY(LtsRestClient)
ADD_SUBDIRECTORY(LtsScriptTimeline)
ADD_SUBDIRECTORY(LtsSerialPortQuerry)
ADD_SUBDIRECTORY(LtsSession)
ADD_SUBDIRECTORY(LtsTimeline)
ADD_SUBDIRECTORY(LtsUtils)
ADD_SUBDIRECTORY(LtsVirtualKeyboard)
ADD_SUBDIRECTORY(LtsWebSocket)
ADD_SUBDIRECTORY(LtsWidgets)
ADD_SUBDIRECTORY(PluginInterfaces)
ADD_SUBDIRECTORY(ProjectConfig)
ADD_SUBDIRECTORY(TopicEditorBase)
endif()
@@ -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
@@ -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()
@@ -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,34 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(CommonUnitTestsPlaceholder)
include(FetchContent)
option(BUILD_SMOKE_TESTS "Build QtTest smoke tests" OFF)
option(BUILD_GTEST_SMOKE "Build GTest smoke tests" OFF)
add_subdirectory(mocks)
if(BUILD_SMOKE_TESTS)
if(NOT TARGET Qt6::Test)
find_package(Qt6 REQUIRED COMPONENTS Test)
endif()
add_executable(coordTransTests_smoke coordTransTests_smoke.cpp)
target_link_libraries(coordTransTests_smoke PRIVATE Qt6::Test MockCoordTrans::MockCoordTrans)
add_test(NAME coordTransTests_smoke COMMAND coordTransTests_smoke)
endif()
if(BUILD_GTEST_SMOKE)
find_package(GTest QUIET)
if(NOT GTest_FOUND)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
endif()
add_executable(coordTransTests_gtest coordTransTests_gtest.cpp)
target_link_libraries(coordTransTests_gtest PRIVATE GTest::gtest_main MockCoordTrans::MockCoordTrans)
add_test(NAME coordTransTests_gtest COMMAND coordTransTests_gtest)
endif()
@@ -1,11 +0,0 @@
// Stub GTest smoke test for coordTrans
#include <gtest/gtest.h>
TEST(CoordTransSmokeTest, StubTest) {
EXPECT_TRUE(true);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -1,14 +0,0 @@
// Stub QtTest smoke test for coordTrans
#include <QTest>
class CoordTransSmokeTest : public QObject
{
Q_OBJECT
private slots:
void stubTest() {
QVERIFY(true);
}
};
QTEST_APPLESS_MAIN(CoordTransSmokeTest)
@@ -1,5 +0,0 @@
add_library(MockCoordTrans STATIC MockCoordTrans.h)
target_include_directories(MockCoordTrans PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# Export header-only semantics by providing an interface target
add_library(MockCoordTrans::MockCoordTrans ALIAS MockCoordTrans)
@@ -1,19 +0,0 @@
// MockCoordTrans.h - Stub mock for CoordTrans functionality
// This is a placeholder to allow builds without full CoordTrans implementation
#ifndef MOCK_COORD_TRANS_H
#define MOCK_COORD_TRANS_H
#include <QtCore/QString>
#include <QtCore/QPointF>
// Minimal stub to satisfy linker requirements
namespace LtsCoordTrans {
// Placeholder types
struct UtmCoordinate {
static UtmCoordinate fromMgrs(const QString&) { return UtmCoordinate(); }
QPointF geographic() const { return QPointF(); }
};
}
#endif // MOCK_COORD_TRANS_H
@@ -1,88 +0,0 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Created by Mike Wille, SLSE, 2016-10-24
#
# LtsAppDescriptor (lib)
# Make ${Qt6_Widgets} optional
if(QT6_GUI_AVAILABLE AND TARGET Qt6::Widgets)
set(Qt6_Widgets ${Qt6_Widgets})
else()
set(Qt6_Widgets "")
endif()
SET(LtsAppDescriptor_SRCS
lts_app_descriptor.cpp
lts_app_descriptor.h
)
NGL_ADD_LIBRARY2(WS LtsAppDescriptor SHARED ${LtsAppDescriptor_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppDescriptor
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptor
PUBLIC
Qt6::Core
)
NGL_INSTALL_TARGET(LtsAppDescriptor)
# LtsAppDescriptorChecker
SET(LtsAppDescriptorChecker_SRCS
checker/main.cpp
)
NGL_ADD_EXECUTABLE(WS LtsAppDescriptorChecker ${LtsAppDescriptorChecker_SRCS})
NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptorChecker
PRIVATE
LtsAppDescriptor
PUBLIC
Qt6::Core
)
# LtsAppDescriptorEditor => Hat niemand benutzt, wird keiner benutzen, also wird der bis auf Weiteres auch nicht an die neue Syntax angepasst.
#SET(LtsAppDescriptorEditor_SRCS
# lts_app_descriptor_editor.cpp
# lts_app_descriptor_editor.h
#)
#NGL_WRAP_UI(LtsAppDescriptorEditor_SRCS
# lts_app_descriptor_editor.ui
#)
#NGL_ADD_LIBRARY2(WS NO_TRANSLATION LtsAppDescriptorEditor SHARED ${LtsAppDescriptorEditor_SRCS})
#NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppDescriptorEditor
# PUBLIC
# ${CMAKE_CURRENT_SOURCE_DIR}/../
#)
if(QT6_GUI_AVAILABLE)
#NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptorEditor
# PUBLIC
# Qt6::Core
# ${Qt6_Widgets}
# LtsAppDescriptor
#)
endif()
#NGL_INSTALL_TARGET(LtsAppDescriptorEditor) #not visible for end-user
## Executable:
#NGL_ADD_EXECUTABLE(WS NO_TRANSLATION LtsAppDescriptorEditorApp main.cpp)
#NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptorEditorApp LtsAppDescriptorEditor)
@@ -1,118 +0,0 @@
#include <LtsAppDescriptor/lts_app_descriptor.h>
#include <QtCore/QCoreApplication>
#include <QDirIterator>
#include <QtCore/QFile>
#include <QFileInfoList>
#include <QtCore/QRegularExpression>
#include <QtCore/QRegularExpressionMatch>
#include <QtCore/QTextStream>
#include <cstdio>
#include <utility>
int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
const QStringList args(a.arguments());
if (args.count() != 3)
{
printf("usage: %s <current source directory> <apps.json>\n", args.at(0).toStdString().c_str());
return -1;
}
const QString currentSrcDir(a.arguments().at(1));
const QString appsJsonFilePath(QStringLiteral("%1/%2").arg(currentSrcDir, a.arguments().at(2)));
QFile f(appsJsonFilePath);
if (!f.open(QFile::ReadOnly))
{
printf("LtsAppDescriptorChecker: cannot open apps.json at %s: %s\n", appsJsonFilePath.toStdString().c_str(),
f.errorString().toStdString().c_str());
return -2;
}
printf("LtsAppDescriptorChecker: checking %s\n", appsJsonFilePath.toStdString().c_str());
QStringList lastErrors;
QStringList dummy1;
QString dummy2;
LtsAppDescriptor::AppDescriptionVector adv =
LtsAppDescriptor::LtsAppDescriptor::getAppDescriptionVector(f.readAll(), lastErrors, dummy1, dummy2);
if (!lastErrors.isEmpty())
{
printf("LtsAppDescriptorChecker: apps.json at %s invalid:\n%s\n", appsJsonFilePath.toStdString().c_str(),
lastErrors.join(QStringLiteral("\r\n\r\n")).toStdString().c_str());
return 1;
}
QFileInfoList guiPluginFiles;
QDirIterator it(QStringLiteral("%1/../../Plugins/").arg(currentSrcDir), QDirIterator::Subdirectories);
while (it.hasNext())
{
QString filename(it.next());
QFileInfo file(filename);
if (file.isDir())
continue;
if (file.fileName().endsWith(QStringLiteral("_gui_plugin.h"), Qt::CaseInsensitive))
guiPluginFiles.append(file);
}
QStringList filenames;
filenames.reserve(guiPluginFiles.size());
for (const QFileInfo& fi : std::as_const(guiPluginFiles))
{
filenames += fi.absoluteFilePath();
}
QStringList guiPluginIIDs;
for (const QFileInfo& fi : std::as_const(guiPluginFiles))
{
QFile f2(fi.absoluteFilePath());
if (!f2.open(QFile::ReadOnly))
continue;
QTextStream ts(&f2);
while (!ts.atEnd())
{
QString line(ts.readLine().trimmed().simplified());
static QString iidIdentifier(QStringLiteral("Q_PLUGIN_METADATA"));
if (line.contains(iidIdentifier))
{
QRegularExpression re(QStringLiteral("(?<=gui:3:).*(?=\"\\))"));
QRegularExpressionMatch match = re.match(line);
if (match.hasMatch())
{
QString matched = match.captured(0);
guiPluginIIDs += matched;
}
break;
}
}
}
bool ok = true;
for (const LtsAppDescriptor::AppDescription& appdesc : std::as_const(adv))
{
for (const LtsAppDescriptor::PluginInfo& pi : std::as_const(appdesc.pluginInfos))
{
if (!guiPluginIIDs.contains(pi.iid))
{
printf("LtsAppDescriptorChecker: apps.json at '%s' uses unknown IID '%s'\n",
appsJsonFilePath.toStdString().c_str(), pi.iid.toStdString().c_str());
ok = false;
}
}
}
return !ok;
}
@@ -1,206 +0,0 @@
#include "lts_app_descriptor.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QJsonParseError>
#include <QtCore/QJsonValue>
namespace LtsAppDescriptor
{
static void addOptionToPlugin(PluginInfo& pluginInfo, const PluginInfo::Flag& flag)
{
pluginInfo.flags |= flag;
}
static void addOptionToPlugin(QVector<PluginInfo>& pluginInfos, const PluginInfo::Flag& flag, const QString& pluginName)
{
for (PluginInfo& pi : pluginInfos)
{
if (pi.iid == pluginName)
{
addOptionToPlugin(pi, flag);
return;
}
}
PluginInfo pi;
pi.iid = pluginName;
pi.type = PluginInfo::PluginType::GUI;
addOptionToPlugin(pi, flag);
pluginInfos.append(pi);
}
static bool addOptionsToPlugin(AppDescription& appDesc, const QString& optionIdentifier,
const QStringList& ignoredPlugins, const QJsonObject& optionsObject,
const PluginInfo::Flag flag, QStringList& lastErrors, bool atLeastOnePluginNeeded)
{
bool hasPlugins = false;
auto optionsObjectIt = optionsObject.constFind(optionIdentifier);
if (optionsObjectIt != optionsObject.constEnd())
{
const QJsonArray plugins((*optionsObjectIt).toObject()[QStringLiteral("Plugins")].toArray());
for (const auto& pluginElem : plugins)
{
const QString pluginName = pluginElem.toString();
if (!ignoredPlugins.contains(pluginName))
{
addOptionToPlugin(appDesc.pluginInfos, flag, pluginName);
hasPlugins = true;
}
}
}
if (atLeastOnePluginNeeded && !hasPlugins)
{
lastErrors.append(QCoreApplication::tr("No plugins specified for '%1' option for app '%2'")
.arg(optionIdentifier, appDesc.appName));
return false;
}
return true;
}
App LtsAppDescriptor::appFromInteger(int i)
{
switch (i)
{
case int(App::Login):
case int(App::Situation):
case int(App::ResourceManager):
case int(App::PlanEditor):
case int(App::BuildingViewer):
case int(App::OC):
case int(App::OrgChart):
case int(App::Scripts):
case int(App::Snapshot):
case int(App::SystemMonitor):
case int(App::Timeline):
case int(App::SymbolEditor):
case int(App::ScriptEditorEssentialsView):
case int(App::BaseDownloader):
case int(App::OnlineHelp):
case int(App::ThpSituationReplay):
case int(App::SituationTablet):
case int(App::SilhouetteEditor):
case int(App::Shootback):
case int(App::SAAs):
return static_cast<App>(i);
}
Q_ASSERT_X(false, "LtsAppDescriptor::appFromInteger", qPrintable(QStringLiteral("Unknown app id: %1").arg(i)));
return App::Login;
}
AppDescriptionVector LtsAppDescriptor::getAppDescriptionVector(const QByteArray& jsonDocument, QStringList& lastErrors,
const QStringList& ignoredPlugins,
QString& loginPluginIid)
{
AppDescriptionVector appDescriptions;
QJsonParseError parseError;
QJsonDocument appsDoc(QJsonDocument::fromJson(jsonDocument, &parseError));
if (parseError.error != QJsonParseError::NoError)
{
int row = 0;
int column = 0;
QChar x = QLatin1Char('\n');
for (int i = 0; i < jsonDocument.size(); i++)
{
if (i == parseError.offset)
break;
if (QChar::fromLatin1(jsonDocument[i]) == x)
{
row++;
column = 0;
}
else
{
column++;
}
}
lastErrors.append(QCoreApplication::tr("JSON: %1<br>Row: %2, Column: %3")
.arg(parseError.errorString())
.arg(row + 1)
.arg(column + 1));
return appDescriptions;
}
const QJsonObject root(appsDoc.object());
loginPluginIid = root[QStringLiteral("LoginPlugin")].toString();
const QJsonArray appArray(root[QStringLiteral("Apps")].toArray());
if (appArray.isEmpty())
{
lastErrors.append(QCoreApplication::tr("Invalid json: Could not find 'Apps' array"));
return appDescriptions;
}
for (const auto& appElem : appArray)
{
AppDescription appDesc;
const QJsonObject appObject = appElem.toObject();
appDesc.id = appFromInteger(appObject.value(QStringLiteral("Id")).toInt(-1));
if (appDesc.id == App::Login)
{
lastErrors.append(QCoreApplication::tr("App '%1': no App Id or invalid App Id given").arg(appDesc.appName));
return appDescriptions;
}
appDesc.appName = appObject.value(QStringLiteral("AppName")).toString();
const QJsonValue valueActive = appObject.value(QStringLiteral("Active"));
if (!valueActive.isUndefined())
{
if (valueActive.type() != QJsonValue::Type::Bool)
{
lastErrors.append(QCoreApplication::tr("App '%1': value App.Active is not a bool (%2)")
.arg(appDesc.appName)
.arg((int)valueActive.type()));
return appDescriptions;
}
}
appDesc.active = valueActive.toBool(false);
const QJsonValue valueVisible = appObject.value(QStringLiteral("Visible"));
if (!valueVisible.isUndefined() && valueVisible.type() != QJsonValue::Type::Bool)
{
lastErrors.append(QCoreApplication::tr("App '%1': value App.Visible is not a bool (%2)")
.arg(appDesc.appName)
.arg((int)valueVisible.type()));
return appDescriptions;
}
appDesc.visible = valueVisible.toBool(true);
const QJsonArray array(appObject.value(QStringLiteral("ACLs")).toArray());
for (const QJsonValue& value : array)
appDesc.requiredAcls.append(value.toInt());
const QJsonObject optionsObject(appObject.value(QStringLiteral("Options")).toObject());
addOptionsToPlugin(appDesc, QStringLiteral("Payloads"), ignoredPlugins, optionsObject,
PluginInfo::Flag::PAYLOAD, lastErrors, false);
addOptionsToPlugin(appDesc, QStringLiteral("Actions"), ignoredPlugins, optionsObject, PluginInfo::Flag::ACTIONS,
lastErrors, true);
addOptionsToPlugin(appDesc, QStringLiteral("Main"), ignoredPlugins, optionsObject, PluginInfo::Flag::MAIN,
lastErrors, false);
appDescriptions.append(appDesc);
}
return appDescriptions;
}
} // namespace LtsAppDescriptor
@@ -1,104 +0,0 @@
/**
* @RDE_HEADER@
* App Descriptor - Class to modify App Descriptor JSON Documents
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-10-24
*/
#pragma once
#include <QVector>
#include <QStringList>
#ifndef LTSAPPDESCRIPTOR_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define LTSAPPDESCRIPTOR_EXPORT
# else
# if defined(MAKE_LTSAPPDESCRIPTOR_LIB)
# define LTSAPPDESCRIPTOR_EXPORT Q_DECL_EXPORT
# else
# define LTSAPPDESCRIPTOR_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
namespace LtsAppDescriptor
{
enum class App
{
Login = -1,
Situation = 0,
ResourceManager = 1,
PlanEditor = 2,
BuildingViewer = 3,
OC = 4,
OrgChart = 5,
Scripts = 6,
// merged into Scripts (aka. ScriptEditor) ScriptPlayer = 7,
Snapshot = 8,
SystemMonitor = 9,
Timeline = 10,
SymbolEditor = 11,
ScriptEditorEssentialsView = 12,
BaseDownloader = 13,
OnlineHelp = 14,
ThpSituationReplay = 15,
SituationTablet = 17,
SilhouetteEditor = 18, // for L400P2
Shootback = 19, // for L400P2
SAAs = 20, //Schnittstelle Adapter Adler
};
struct PluginInfo
{
enum class PluginType
{
UNKNOWN,
DATA,
GUI,
GUI_ELEMENT,
};
enum class Flag
{
UNKNOWN = 1 << 0,
ACTIONS = 1 << 1,
MAIN = 1 << 2,
PAYLOAD = 1 << 3,
OPTION = 1 << 4,
};
PluginType type = PluginType::UNKNOWN;
QString iid;
QFlags<Flag> flags;
QStringList options;
};
struct AppDescription
{
App id = App::Login;
bool active = false;
bool visible = true;
QString appName;
QVector<int> requiredAcls;
QVector<PluginInfo> pluginInfos;
bool isValid() const { return !appName.isEmpty(); }
};
typedef QVector<AppDescription> AppDescriptionVector;
namespace LtsAppDescriptor // TODO: make qobject class singleton - for tr
{
LTSAPPDESCRIPTOR_EXPORT App appFromInteger(int i);
LTSAPPDESCRIPTOR_EXPORT AppDescriptionVector getAppDescriptionVector(const QByteArray &jsonDocument, QStringList &lastErrors,
const QStringList &ignoredPlugins, QString &loginPluginIid);
}
} // namespace LtsAppDescriptor
@@ -1,536 +0,0 @@
#include "lts_app_descriptor_editor.h"
#include "ui_lts_app_descriptor_editor.h"
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtWidgets/QFileDialog>
#include <QtCore/QJsonDocument>
#include <QtWidgets/QMessageBox>
#include <QStringListModel>
#include <QtCore/QTimer>
#include <QtWidgets/QListView>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
LtsAppDescriptorEditor::LtsAppDescriptorEditor(QWidget* parent) : QWidget(parent), m_ui(new Ui_LtsAppDescriptionEditor)
{
m_ui->setupUi(this);
m_vector = LtsAppDescriptor::AppDescriptionVector();
m_appIndex = QModelIndex();
m_pluginIndex = QModelIndex();
m_fileName = QString();
m_changed = false;
connect(m_ui->lvApps, &QListView::clicked, this, &LtsAppDescriptorEditor::onAppsClicked);
connect(m_ui->lvPlugins, &QListView::clicked, this, &LtsAppDescriptorEditor::onPluginsClicked);
connect(m_ui->lePluginType, &QLineEdit::editingFinished, this, &LtsAppDescriptorEditor::pluginTypeChanged);
connect(m_ui->pbAddApp, &QPushButton::pressed, this, &LtsAppDescriptorEditor::onAppAddClicked);
connect(m_ui->pbAddPlugin, &QPushButton::pressed, this, &LtsAppDescriptorEditor::onPluginAddClicked);
connect(m_ui->pbDeleteApp, &QPushButton::pressed, this, &LtsAppDescriptorEditor::onAppDeleteClicked);
connect(m_ui->pbDeletePlugin, &QPushButton::pressed, this, &LtsAppDescriptorEditor::onPluginDeletedClicked);
connect(m_ui->pbUpdate, &QPushButton::pressed, this, &LtsAppDescriptorEditor::onUpdateClicked);
connect(m_ui->pbNewPlugin, &QPushButton::pressed, this, &LtsAppDescriptorEditor::onNewpluginClicked);
connect(m_ui->pbNewApp, &QPushButton::pressed, this, &LtsAppDescriptorEditor::onNewAppClicked);
connect(m_ui->lePluginName, &QLineEdit::textChanged, this, &LtsAppDescriptorEditor::enableNewPlugin);
connect(m_ui->lePluginType, &QLineEdit::textChanged, this, &LtsAppDescriptorEditor::enableNewPlugin);
m_ui->gbPlugins->setEnabled(false);
m_ui->pbDeletePlugin->setEnabled(false);
m_ui->pbDeleteApp->setEnabled(false);
m_ui->pbAddPlugin->setEnabled(false);
m_ui->pbUpdate->setEnabled(false);
m_ui->pbNewPlugin->setEnabled(false);
m_ui->pbNewApp->setEnabled(false);
QTimer::singleShot(0, this, &LtsAppDescriptorEditor::getAppDescriptionVector);
}
LtsAppDescriptorEditor::~LtsAppDescriptorEditor()
{
checkSave();
delete m_ui;
}
void LtsAppDescriptorEditor::getAppDescriptionVector()
{
m_fileName = QFileDialog::getOpenFileName(
this, tr("Open JSON"), QLatin1Literal("/shd/CTC/TOOLS/TDS/TDS-source/Lts/Projects/Legatus/resources/"),
tr("JSON File (*.json)"));
if (m_fileName.isEmpty())
return;
QFile file;
file.setFileName(m_fileName);
file.open(QIODevice::ReadOnly | QIODevice::Text);
QString val(QString::fromUtf8(file.readAll()));
file.close();
QStringList errors;
m_vector = LtsAppDescriptor::LtsAppDescriptor::getAppDescriptionVector(val.toUtf8(), errors);
updateAppModel();
}
void LtsAppDescriptorEditor::onAppsClicked(const QModelIndex& index)
{
m_ui->gbPlugins->setEnabled(true);
m_ui->pbDeleteApp->setEnabled(true);
m_ui->pbAddPlugin->setEnabled(true);
m_ui->pbNewApp->setEnabled(true);
m_appIndex = index;
QStringList pluginList;
Q_FOREACH (const LtsAppDescriptor::PluginInfo& reqPlugin, m_vector.at(m_appIndex.row()).requiredPlugins)
{
LtsAppDescriptor::PluginInfo::PluginType pluginType = reqPlugin.type;
QString type;
if (pluginType == LtsAppDescriptor::PluginInfo::PluginType::DATA)
type = QLatin1String("data");
else if (pluginType == LtsAppDescriptor::PluginInfo::PluginType::GUI)
type = QLatin1String("gui");
else
type = QLatin1String("unknown");
if (type == QLatin1String("data"))
pluginList << QString(type + QLatin1String(": ") + reqPlugin.iid);
else
{
QString strFlags;
QFlags<LtsAppDescriptor::PluginInfo::Flag> flags = reqPlugin.flags;
bool actionsFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::ACTIONS);
if (actionsFlag)
strFlags.append(QLatin1String("actions "));
bool mainFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN);
if (mainFlag)
strFlags.append(QLatin1String("main "));
bool payloadFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::PAYLOAD);
if (payloadFlag)
strFlags.append(QLatin1String("payload "));
bool optionalFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::OPTION);
if (optionalFlag)
strFlags.append(QLatin1String("optional "));
pluginList << QString(type + QLatin1String(": ") + reqPlugin.iid + QLatin1String(" [ ") + strFlags +
QLatin1String("]"));
}
}
updatePluginModel(pluginList);
m_ui->chbAppActive->setChecked(m_vector.at(m_appIndex.row()).active);
m_ui->leAppName->setText(m_vector.at(m_appIndex.row()).appName);
}
void LtsAppDescriptorEditor::onPluginsClicked(const QModelIndex& index)
{
m_pluginIndex = index;
m_ui->pbDeletePlugin->setEnabled(true);
m_ui->pbUpdate->setEnabled(true);
m_ui->pbNewPlugin->setEnabled(true);
const LtsAppDescriptor::PluginInfo plugin = m_vector.at(m_appIndex.row()).requiredPlugins.at(m_pluginIndex.row());
QString pluginName = plugin.iid;
m_ui->lePluginName->setText(pluginName);
LtsAppDescriptor::PluginInfo::PluginType pluginType = plugin.type;
QString type;
if (pluginType == LtsAppDescriptor::PluginInfo::PluginType::DATA)
type = QLatin1String("data");
else if (pluginType == LtsAppDescriptor::PluginInfo::PluginType::GUI)
type = QLatin1String("gui");
else
type = QLatin1String("unknown");
m_ui->lePluginType->setText(type);
if (type == QLatin1String("gui"))
{
m_ui->chbActions->setEnabled(true);
m_ui->chbMain->setEnabled(true);
m_ui->chbPayload->setEnabled(true);
QFlags<LtsAppDescriptor::PluginInfo::Flag> flags = plugin.flags;
bool actionsFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::ACTIONS);
m_ui->chbActions->setChecked(actionsFlag);
bool mainFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN);
m_ui->chbMain->setChecked(mainFlag);
bool payloadFlag = flags.testFlag(LtsAppDescriptor::PluginInfo::Flag::PAYLOAD);
m_ui->chbPayload->setChecked(payloadFlag);
}
else
{
m_ui->chbActions->setChecked(false);
m_ui->chbActions->setEnabled(false);
m_ui->chbMain->setChecked(false);
m_ui->chbMain->setEnabled(false);
m_ui->chbPayload->setChecked(false);
m_ui->chbPayload->setEnabled(false);
}
}
void LtsAppDescriptorEditor::pluginTypeChanged()
{
if (m_ui->lePluginType->text().isEmpty())
return;
if (m_ui->lePluginType->text() == QLatin1String("gui"))
{
m_ui->chbActions->setEnabled(true);
m_ui->chbMain->setEnabled(true);
m_ui->chbPayload->setEnabled(true);
}
else if (m_ui->lePluginType->text() == QLatin1String("data"))
{
m_ui->chbActions->setChecked(false);
m_ui->chbActions->setEnabled(false);
m_ui->chbMain->setChecked(false);
m_ui->chbMain->setEnabled(false);
m_ui->chbPayload->setChecked(false);
m_ui->chbPayload->setEnabled(false);
}
else
{
QMessageBox msgBox(QMessageBox::Information, tr("Error"),
tr("Plugin Type must either be \"gui\" or \"data\"."));
msgBox.exec();
m_ui->lePluginType->setText(QString());
}
}
void LtsAppDescriptorEditor::onAppAddClicked()
{
m_changed = true;
if (m_ui->leAppName->text().isEmpty())
{
QMessageBox msgBox(QMessageBox::Critical, tr("Error"), tr("The App needs to have a name."));
msgBox.exec();
return;
}
QStringList appList;
Q_FOREACH (const LtsAppDescriptor::AppDescription& appDesc, m_vector)
{
appList << appDesc.appName;
}
if (appList.contains(m_ui->leAppName->text(), Qt::CaseInsensitive))
{
QMessageBox msgBox(QMessageBox::Critical, tr("Error"),
tr("There already exists an app named \"%1\".").arg(m_ui->leAppName->text()));
msgBox.exec();
return;
}
LtsAppDescriptor::AppDescription newApp;
newApp.appName = m_ui->leAppName->text();
newApp.active = m_ui->chbAppActive->isChecked();
m_vector.append(newApp);
updateAppModel();
}
void LtsAppDescriptorEditor::onPluginAddClicked()
{
m_changed = true;
if (m_ui->lePluginType->text().isEmpty())
{
QMessageBox msgBox(QMessageBox::Critical, tr("Error"), tr("The Plugin needs to contain a type."));
msgBox.exec();
return;
}
if (m_ui->lePluginName->text().isEmpty())
{
QMessageBox msgBox(QMessageBox::Critical, tr("Error"), tr("The Plugin needs to have a name."));
msgBox.exec();
return;
}
QStringList pluginList;
Q_FOREACH (const LtsAppDescriptor::PluginInfo& pluginDesc, m_vector.at(m_appIndex.row()).requiredPlugins)
{
pluginList << pluginDesc.iid;
}
if (pluginList.contains(m_ui->lePluginName->text(), Qt::CaseInsensitive))
{
QMessageBox msgBox(QMessageBox::Critical, tr("Error"),
tr("There already exists an plugin named \"%1\".").arg(m_ui->lePluginName->text()));
msgBox.exec();
return;
}
LtsAppDescriptor::PluginInfo newPlugin;
newPlugin.iid = m_ui->lePluginName->text();
if (m_ui->lePluginType->text() == QLatin1String("gui"))
newPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::GUI;
else if (m_ui->lePluginType->text() == QLatin1String("data"))
newPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::DATA;
else
newPlugin.type = LtsAppDescriptor::PluginInfo::PluginType::UNKNOWN;
if (m_ui->chbActions->isChecked())
newPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::ACTIONS;
if (m_ui->chbMain->isChecked())
newPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::MAIN;
if (m_ui->chbPayload->isChecked())
newPlugin.flags |= LtsAppDescriptor::PluginInfo::Flag::PAYLOAD;
m_vector[m_appIndex.row()].requiredPlugins.append(newPlugin);
onAppsClicked(m_appIndex);
onNewpluginClicked();
}
void LtsAppDescriptorEditor::onAppDeleteClicked()
{
m_changed = true;
if (!m_appIndex.isValid())
return;
QMessageBox msgBox(tr("Warning"),
tr("The App \n\"%1\" \nwill be removed. Continue?").arg(m_appIndex.data().toString()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
QMessageBox::No | QMessageBox::Escape, QMessageBox::NoButton);
if (msgBox.exec() == QMessageBox::No)
return;
m_vector.remove(m_appIndex.row());
updateAppModel();
}
void LtsAppDescriptorEditor::onPluginDeletedClicked()
{
m_changed = true;
if (!m_pluginIndex.isValid())
return;
QMessageBox msgBox(tr("Warning"),
tr("The Plugin \n\"%1\" \nwill be removed. Continue?").arg(m_pluginIndex.data().toString()),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
QMessageBox::No | QMessageBox::Escape, QMessageBox::NoButton);
if (msgBox.exec() == QMessageBox::No)
return;
m_vector[m_appIndex.row()].requiredPlugins.remove(m_pluginIndex.row());
m_pluginIndex = QModelIndex();
m_ui->lePluginName->setText(QString());
m_ui->lePluginType->setText(QString());
m_ui->chbActions->setChecked(false);
m_ui->chbMain->setChecked(false);
m_ui->chbPayload->setChecked(false);
m_ui->pbDeletePlugin->setEnabled(false);
m_ui->pbUpdate->setEnabled(false);
m_ui->pbNewPlugin->setEnabled(false);
onAppsClicked(m_appIndex);
}
void LtsAppDescriptorEditor::onUpdateClicked()
{
m_changed = true;
if (m_ui->lePluginType->text().isEmpty())
{
QMessageBox msgBox(QMessageBox::Critical, tr("Error"), tr("The Plugin needs to contain a type."));
msgBox.exec();
return;
}
if (m_ui->lePluginName->text().isEmpty())
{
QMessageBox msgBox(QMessageBox::Critical, tr("Error"), tr("The Plugin needs to have a name."));
msgBox.exec();
return;
}
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].iid = m_ui->lePluginName->text();
if (m_ui->lePluginType->text() == QLatin1String("gui"))
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].type =
LtsAppDescriptor::PluginInfo::PluginType::GUI;
else if (m_ui->lePluginType->text() == QLatin1String("data"))
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].type =
LtsAppDescriptor::PluginInfo::PluginType::DATA;
else
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].type =
LtsAppDescriptor::PluginInfo::PluginType::UNKNOWN;
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags =
QFlags<LtsAppDescriptor::PluginInfo::Flag>();
if (m_ui->chbActions->isChecked())
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags |=
LtsAppDescriptor::PluginInfo::Flag::ACTIONS;
if (m_ui->chbMain->isChecked())
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags |=
LtsAppDescriptor::PluginInfo::Flag::MAIN;
if (m_ui->chbPayload->isChecked())
m_vector[m_appIndex.row()].requiredPlugins[m_pluginIndex.row()].flags |=
LtsAppDescriptor::PluginInfo::Flag::PAYLOAD;
onAppsClicked(m_appIndex);
onNewpluginClicked();
}
void LtsAppDescriptorEditor::onNewpluginClicked()
{
m_ui->lePluginName->setText(QString());
m_ui->lePluginType->setText(QString());
m_ui->chbActions->setEnabled(true);
m_ui->chbMain->setEnabled(true);
m_ui->chbPayload->setEnabled(true);
m_ui->chbActions->setChecked(false);
m_ui->chbMain->setChecked(false);
m_ui->chbPayload->setChecked(false);
m_ui->pbUpdate->setEnabled(false);
m_ui->pbNewPlugin->setEnabled(false);
m_ui->pbDeletePlugin->setEnabled(false);
if (m_pluginIndex.isValid())
m_ui->lvPlugins->selectionModel()->select(m_pluginIndex, QItemSelectionModel::Deselect);
m_pluginIndex = QModelIndex();
}
void LtsAppDescriptorEditor::onNewAppClicked()
{
updatePluginModel(QStringList());
m_ui->gbPlugins->setEnabled(false);
m_ui->pbDeletePlugin->setEnabled(false);
m_ui->pbDeleteApp->setEnabled(false);
m_ui->pbAddPlugin->setEnabled(false);
m_ui->pbUpdate->setEnabled(false);
m_ui->pbNewPlugin->setEnabled(false);
m_ui->leAppName->setText(QString());
m_ui->chbAppActive->setChecked(false);
m_ui->lePluginName->setText(QString());
m_ui->lePluginType->setText(QString());
m_ui->chbActions->setChecked(false);
m_ui->chbMain->setChecked(false);
m_ui->chbPayload->setChecked(false);
m_ui->pbNewApp->setEnabled(false);
if (m_appIndex.isValid())
m_ui->lvApps->selectionModel()->select(m_appIndex, QItemSelectionModel::Deselect);
m_appIndex = QModelIndex();
}
void LtsAppDescriptorEditor::updateAppModel()
{
onNewAppClicked();
QStringList appList;
Q_FOREACH (const LtsAppDescriptor::AppDescription& appDesc, m_vector)
{
appList << appDesc.appName;
}
if (!m_ui->lvApps->model())
m_ui->lvApps->setModel(new QStringListModel(this));
QStringListModel* m = qobject_cast<QStringListModel*>(m_ui->lvApps->model());
if (!m)
return;
m->setStringList(appList);
}
void LtsAppDescriptorEditor::updatePluginModel(const QStringList& list)
{
if (!m_ui->lvPlugins->model())
m_ui->lvPlugins->setModel(new QStringListModel(this));
QStringListModel* m = qobject_cast<QStringListModel*>(m_ui->lvPlugins->model());
if (!m)
return;
m->setStringList(list);
}
void LtsAppDescriptorEditor::checkSave()
{
if (m_changed)
{
QMessageBox msgBox(tr("Warning"), tr("The Data has been modified.\nDo you want to save your changes?"),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
QMessageBox::No | QMessageBox::Escape, QMessageBox::NoButton);
if (msgBox.exec() == QMessageBox::Yes)
onSaveAsClicked();
}
}
void LtsAppDescriptorEditor::onSaveAsClicked()
{
QString fileName = QFileDialog::getSaveFileName(
this, tr("Save JSON"), QLatin1Literal("/shd/CTC/TOOLS/TDS/TDS-source/Lts/Projects/Legatus/resources/"),
tr("JSON File (*.json)"));
if (fileName.isEmpty())
return;
QByteArray jsonByteArray;
jsonByteArray = LtsAppDescriptor::LtsAppDescriptor::createJson(m_vector);
QFile f(fileName);
f.open(QIODevice::WriteOnly);
f.write(jsonByteArray);
f.close();
m_changed = false;
QMessageBox msgBox(QMessageBox::Information, tr("Success"), tr("Your file has been saved."));
msgBox.exec();
}
void LtsAppDescriptorEditor::onSaveClicked()
{
const QByteArray jsonByteArray(LtsAppDescriptor::LtsAppDescriptor::createJson(m_vector));
QFile f(m_fileName);
f.open(QIODevice::WriteOnly);
f.write(jsonByteArray);
f.close();
m_changed = false;
QMessageBox msgBox(QMessageBox::Information, tr("Success"), tr("Your file has been saved."));
msgBox.exec();
}
void LtsAppDescriptorEditor::onOpenClicked()
{
checkSave();
getAppDescriptionVector();
m_changed = false;
}
void LtsAppDescriptorEditor::onNewClicked()
{
checkSave();
m_ui->chbActions->setChecked(false);
m_ui->chbMain->setChecked(false);
m_ui->chbPayload->setChecked(false);
m_ui->chbAppActive->setChecked(false);
m_ui->leAppName->setText(QString());
m_ui->lePluginName->setText(QString());
m_ui->lePluginType->setText(QString());
m_ui->gbPlugins->setEnabled(false);
m_vector = LtsAppDescriptor::AppDescriptionVector();
updateAppModel();
updatePluginModel(QStringList());
}
void LtsAppDescriptorEditor::enableNewPlugin()
{
m_ui->pbNewPlugin->setEnabled(true);
}
void LtsAppDescriptorEditor::onQuitClicked()
{
QApplication::quit();
}
@@ -1,65 +0,0 @@
#pragma once
#include "lts_app_descriptor.h"
#include <QtCore/QModelIndex>
#include <QtCore/QObject>
#include <QtWidgets/QWidget>
#ifndef LTSAPPDESCRIPTOREDITOR_EXPORT
#ifdef BUILD_STATIC_LIBRARIES
#define LTSAPPDESCRIPTOREDITOR_EXPORT
#else
#if defined(MAKE_LTSAPPDESCRIPTOREDITOR_LIB)
#define LTSAPPDESCRIPTOREDITOR_EXPORT Q_DECL_EXPORT
#else
#define LTSAPPDESCRIPTOREDITOR_EXPORT Q_DECL_IMPORT
#endif
#endif
#endif
class Ui_LtsAppDescriptionEditor;
class LTSAPPDESCRIPTOREDITOR_EXPORT LtsAppDescriptorEditor : public QWidget
{
Q_OBJECT
public:
explicit LtsAppDescriptorEditor(QWidget* parent = 0);
~LtsAppDescriptorEditor();
private:
Ui_LtsAppDescriptionEditor* m_ui;
LtsAppDescriptor::AppDescriptionVector m_vector;
QModelIndex m_appIndex;
QModelIndex m_pluginIndex;
QString m_fileName;
bool m_changed;
void getAppDescriptionVector();
void setupMainWindow();
void updateAppModel();
void updatePluginModel(const QStringList& list);
void checkSave();
private slots:
void onAppsClicked(const QModelIndex& index);
void onPluginsClicked(const QModelIndex& index);
void pluginTypeChanged();
void onAppAddClicked();
void onPluginAddClicked();
void onAppDeleteClicked();
void onPluginDeletedClicked();
void onUpdateClicked();
void onNewpluginClicked();
void onNewAppClicked();
void enableNewPlugin();
public slots:
void onSaveClicked();
void onSaveAsClicked();
void onOpenClicked();
void onNewClicked();
void onQuitClicked();
};
@@ -1,434 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LtsAppDescriptionEditor</class>
<widget class="QWidget" name="LtsAppDescriptionEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>865</width>
<height>337</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string/>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="3" colspan="2">
<widget class="QLabel" name="Plugins">
<property name="text">
<string>Required Plugins:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="AppLabel">
<property name="text">
<string>App:</string>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="4" colspan="3">
<widget class="QListView" name="lvApps">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
</item>
<item row="5" column="7">
<widget class="QPushButton" name="pbNewPlugin">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>New</string>
</property>
</widget>
</item>
<item row="2" column="3" colspan="2">
<widget class="QLabel" name="AppName">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Name:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="5" column="6">
<widget class="QPushButton" name="pbDeletePlugin">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item row="1" column="7" rowspan="4" colspan="3">
<widget class="QGroupBox" name="gbPlugins">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>New Plugin Requirements</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="PluginType">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lePluginType">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>210</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>gui/data</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="PluginName">
<property name="text">
<string>IID:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lePluginName">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>210</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Name of the Plugin</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="PluginFlags">
<property name="text">
<string>Flags:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="chbActions">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>Actions</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="chbMain">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>Main</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="chbPayload">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>Payload</string>
</property>
</widget>
</item>
<item row="6" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="3" colspan="2">
<widget class="QLabel" name="AppActive">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Active:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QPushButton" name="pbDeleteApp">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item row="4" column="3" colspan="4">
<widget class="QListView" name="lvPlugins">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
</item>
<item row="5" column="9">
<widget class="QPushButton" name="pbUpdate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QLineEdit" name="leAppName">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>Name of the App</string>
</property>
</widget>
</item>
<item row="5" column="8">
<widget class="QPushButton" name="pbAddPlugin">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="pbNewApp">
<property name="text">
<string>New</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QCheckBox" name="chbAppActive">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="pbAddApp">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>List of Apps</string>
</property>
</widget>
</item>
<item row="1" column="6" rowspan="3">
<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>
</widget>
<tabstops>
<tabstop>lvApps</tabstop>
<tabstop>pbNewApp</tabstop>
<tabstop>pbAddApp</tabstop>
<tabstop>pbDeleteApp</tabstop>
<tabstop>chbAppActive</tabstop>
<tabstop>leAppName</tabstop>
<tabstop>lvPlugins</tabstop>
<tabstop>pbDeletePlugin</tabstop>
<tabstop>pbNewPlugin</tabstop>
<tabstop>lePluginType</tabstop>
<tabstop>lePluginName</tabstop>
<tabstop>chbPayload</tabstop>
<tabstop>chbMain</tabstop>
<tabstop>chbActions</tabstop>
<tabstop>pbAddPlugin</tabstop>
<tabstop>pbUpdate</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -1,55 +0,0 @@
#include "lts_app_descriptor_editor.h"
#include <QtGui/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
QMainWindow* mainWindow = new QMainWindow();
QMenu* fileMenu = mainWindow->menuBar()->addMenu(QObject::tr("&File"));
QAction* openAct = new QAction(QObject::tr("&Open"), mainWindow);
openAct->setShortcuts(QKeySequence::Open);
openAct->setStatusTip(QObject::tr("Open an existing file"));
fileMenu->addAction(openAct);
QAction* newAct = new QAction(QObject::tr("&New"), mainWindow);
newAct->setShortcuts(QKeySequence::New);
newAct->setStatusTip(QObject::tr("Create a new App"));
fileMenu->addAction(newAct);
QAction* saveAct = new QAction(QObject::tr("&Save"), mainWindow);
saveAct->setShortcuts(QKeySequence::Save);
saveAct->setStatusTip(QObject::tr("Save modified file"));
fileMenu->addAction(saveAct);
QAction* saveAsAct = new QAction(QObject::tr("&SaveAs"), mainWindow);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
saveAsAct->setStatusTip(QObject::tr("Save modified file as"));
fileMenu->addAction(saveAsAct);
QAction* quitAct = new QAction(QObject::tr("&Quit"), mainWindow);
quitAct->setShortcuts(QKeySequence::Quit);
quitAct->setStatusTip(QObject::tr("Quit the Application"));
fileMenu->addAction(quitAct);
LtsAppDescriptorEditor* editor = new LtsAppDescriptorEditor(mainWindow);
mainWindow->setCentralWidget(editor);
QObject::connect(openAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onOpenClicked);
QObject::connect(newAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onNewClicked);
QObject::connect(saveAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onSaveClicked);
QObject::connect(saveAsAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onSaveAsClicked);
QObject::connect(quitAct, &QAction::triggered, editor, &LtsAppDescriptorEditor::onQuitClicked);
mainWindow->show();
int ret = a.exec();
delete mainWindow;
return ret;
}
@@ -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
Loaded 100 of 4557 files, more files were not shown because too many files have changed in this diff. Show more