Adds initial intermediate qt6 porting state
This commit is contained in:
11247 files changed
+1891537
No files matched your search
@@ -2,3 +2,4 @@
|
|||||||
.*~
|
.*~
|
||||||
*~
|
*~
|
||||||
build
|
build
|
||||||
|
**/build_*/
|
||||||
Submodule
+1
Submodule qt5-before/qt5-legatus-legacy added at b7d90b9afe.
+378
@@ -0,0 +1,378 @@
|
|||||||
|
#
|
||||||
|
# Haupt CMakeLists.txt für das Projekt TDS
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
# $HeadURL$
|
||||||
|
# $Author$
|
||||||
|
#
|
||||||
|
# Setzen von CMAKE_INSTALL_PREFIX auf "/shd/CTC" falls nicht über Kommandozeile angegeben
|
||||||
|
#
|
||||||
|
|
||||||
|
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
|
||||||
|
IF(POLICY CMP0069)
|
||||||
|
CMAKE_POLICY(SET CMP0069 NEW) # cmake >= 3.9.0 - INTERPROCEDURAL_OPTIMIZATION behavior
|
||||||
|
ENDIF()
|
||||||
|
IF(POLICY CMP0071)
|
||||||
|
CMAKE_POLICY(SET CMP0071 NEW) # cmake >= 3.10.0 - automatically process generated sources with AUTOMOC
|
||||||
|
ENDIF()
|
||||||
|
IF(POLICY CMP0072)
|
||||||
|
CMAKE_POLICY(SET CMP0072 NEW) # cmake >= 3.11.0 - prefer libOpenGL.so over legacy libGL.so
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
|
||||||
|
IF(NOT DEFINED CMAKE_INSTALL_PREFIX)
|
||||||
|
SET(CMAKE_INSTALL_PREFIX "/shd/CTC" CACHE STRING "Install path prefix, prepended onto install directories." FORCE)
|
||||||
|
ENDIF()
|
||||||
|
#
|
||||||
|
# # Setzen von CMAKE_BUILD_TYPE auf "Debug" falls nicht über Kommandozeile angegeben
|
||||||
|
#
|
||||||
|
IF(NOT DEFINED CMAKE_BUILD_TYPE)
|
||||||
|
SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
||||||
|
ENDIF()
|
||||||
|
ADD_DEFINITIONS(-DEXPORT_BDV_TOPIC_AS_JSON)
|
||||||
|
SET(_ver "1.2.0")
|
||||||
|
IF(DEFINED LTS_VERSION_MAJOR AND DEFINED LTS_VERSION_MINOR AND DEFINED LTS_VERSION_PATCH)
|
||||||
|
SET(_ver "${LTS_VERSION_MAJOR}.${LTS_VERSION_MINOR}.${LTS_VERSION_PATCH}")
|
||||||
|
ENDIF()
|
||||||
|
PROJECT(LTS VERSION ${_ver})
|
||||||
|
MESSAGE(STATUS "LTS Version: ${LTS_VERSION_MAJOR}.${LTS_VERSION_MINOR}.${LTS_VERSION_PATCH}")
|
||||||
|
SET(LTS_VERSION_DATE "01.01.2018" CACHE STRING "The date when this software was compiled")
|
||||||
|
ADD_DEFINITIONS(-DLTS_VERSION_MAJOR=\"${LTS_VERSION_MAJOR}\")
|
||||||
|
ADD_DEFINITIONS(-DLTS_VERSION_MINOR=\"${LTS_VERSION_MINOR}\")
|
||||||
|
ADD_DEFINITIONS(-DLTS_VERSION_PATCH=\"${LTS_VERSION_PATCH}\")
|
||||||
|
ADD_DEFINITIONS(-DLTS_VERSION_DATE=\"${LTS_VERSION_DATE}\")
|
||||||
|
IF(JENKINS_BUILD_NUMBER)
|
||||||
|
ADD_DEFINITIONS(-DJENKINS_BUILD_NUMBER=\"${JENKINS_BUILD_NUMBER}\")
|
||||||
|
ELSE()
|
||||||
|
ADD_DEFINITIONS(-DJENKINS_BUILD_NUMBER=\"unknown\")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
ADD_DEFINITIONS(-DAPP6C)
|
||||||
|
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||||
|
SET(CMAKE_CXX_STANDARD 20)
|
||||||
|
SET(CMAKE_CXX_STANDARD_REQUIRED 14)
|
||||||
|
SET(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
|
||||||
|
SET(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||||
|
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
|
IF(CMAKE_VERSION VERSION_GREATER 3.15.9)
|
||||||
|
OPTION(USE_UNITY_BUILDS "Use Unity builds to speed up compilation (cmake 3.16 or newer required!)" OFF)
|
||||||
|
IF (USE_UNITY_BUILDS)
|
||||||
|
SET(CMAKE_UNITY_BUILD ON)
|
||||||
|
SET(CMAKE_UNITY_BUILD_BATCH_SIZE 40)
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(WIN32)
|
||||||
|
SET(EXTERNAL_LIBRARY_DIR "C:/Development/Projekte/SituationMap/ExtFw" CACHE STRING "Library dir with all external dependencies")
|
||||||
|
LIST(APPEND CMAKE_PREFIX_PATH "${EXTERNAL_LIBRARY_DIR}")
|
||||||
|
LIST(APPEND CMAKE_PROGRAM_PATH "${EXTERNAL_LIBRARY_DIR}/bin;${EXTERNAL_LIBRARY_DIR}/dev-utils/bin")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
ENABLE_TESTING()
|
||||||
|
INCLUDE(CTest)
|
||||||
|
CONFIGURE_FILE(CTestCustom.ctest ${CMAKE_BINARY_DIR}/CTestCustom.ctest COPYONLY)
|
||||||
|
CONFIGURE_FILE(lts-cc.conf ${CMAKE_BINARY_DIR}/lts-cc.conf.distrib COPYONLY)
|
||||||
|
|
||||||
|
SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
IF(WIN32)
|
||||||
|
SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
ELSE(WIN32)
|
||||||
|
SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Disallow in-source build
|
||||||
|
#
|
||||||
|
INCLUDE(MacroEnsureOutOfSourceBuild)
|
||||||
|
|
||||||
|
macro_ensure_out_of_source_build("In-Source build nicht erlaubt!")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Qt ist wichtig - zuerst suchen
|
||||||
|
#
|
||||||
|
SET(CMAKE_AUTOMOC TRUE)
|
||||||
|
SET(CMAKE_AUTOUIC TRUE)
|
||||||
|
SET(QT_USE_IMPORTED_TARGETS TRUE)
|
||||||
|
# Prefer Qt6, fallback to Qt5. Create Qt5::*-aliases when Qt6 is found so existing CMakeLists continue to work.
|
||||||
|
# Try to detect Qt6 with a minimal check and then create aliases/placeholders for components used across the project.
|
||||||
|
# Prefer Qt6, fallback to Qt5. Create Qt5::* aliases only when the corresponding
|
||||||
|
# Qt6:: component target exists. Avoid creating IMPORTED placeholder targets which
|
||||||
|
# have no LOCATION and later inject 'NOTFOUND' into autogen timestamp deps.
|
||||||
|
# Find minimal Qt6 Core first, then probe for other components (non-fatal).
|
||||||
|
find_package(Qt6 6.8 COMPONENTS Core QUIET)
|
||||||
|
if(Qt6_FOUND)
|
||||||
|
message(STATUS "Found Qt6: ${Qt6Core_VERSION}")
|
||||||
|
set(_qlist Concurrent Core Gui Location LinguistTools Network OpenGL PrintSupport
|
||||||
|
Sensors Multimedia Sql Svg SerialPort Test UiTools WebSockets WebEngineCore WebEngineWidgets
|
||||||
|
Widgets Xml XmlPatterns)
|
||||||
|
foreach(_comp ${_qlist})
|
||||||
|
# Probe for each component (non-fatal). If found, create a Qt5:: alias.
|
||||||
|
find_package(Qt6 COMPONENTS ${_comp} QUIET)
|
||||||
|
if(TARGET "Qt6::${_comp}")
|
||||||
|
add_library("Qt5::${_comp}" ALIAS "Qt6::${_comp}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
# Map version variable for compatibility if available
|
||||||
|
if(Qt6Core_VERSION)
|
||||||
|
set(Qt5Core_VERSION ${Qt6Core_VERSION} CACHE INTERNAL "Compatibility Qt5Core_VERSION mapped from Qt6")
|
||||||
|
endif()
|
||||||
|
# Find rcc/lrelease programs as compatibility fallbacks
|
||||||
|
find_program(_rcc_exec rcc-qt6 rcc)
|
||||||
|
if(_rcc_exec)
|
||||||
|
set(Qt5Core_RCC_EXECUTABLE ${_rcc_exec} CACHE INTERNAL "Compatibility rcc executable")
|
||||||
|
endif()
|
||||||
|
find_program(_lrelease_exec lrelease-qt6 lrelease)
|
||||||
|
if(_lrelease_exec)
|
||||||
|
set(Qt5_LRELEASE_EXECUTABLE ${_lrelease_exec} CACHE INTERNAL "Compatibility lrelease executable")
|
||||||
|
if(TARGET Qt6::LinguistTools AND NOT TARGET Qt5::lrelease)
|
||||||
|
add_executable(Qt5::lrelease IMPORTED)
|
||||||
|
set_target_properties(Qt5::lrelease PROPERTIES IMPORTED_LOCATION ${Qt5_LRELEASE_EXECUTABLE})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
FIND_PACKAGE(Qt5 5.9.0 COMPONENTS Concurrent Core Gui Location LinguistTools
|
||||||
|
Network OpenGL PrintSupport Sensors Multimedia
|
||||||
|
Sql Svg SerialPort Test UiTools WebSockets
|
||||||
|
WebEngineCore WebEngineWidgets Widgets
|
||||||
|
Xml XmlPatterns
|
||||||
|
REQUIRED)
|
||||||
|
MESSAGE(STATUS "Qt version is ${Qt5Core_VERSION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
MESSAGE(STATUS "Qt version is ${Qt5Core_VERSION}")
|
||||||
|
ADD_DEFINITIONS(-DQT_USE_QSTRINGBUILDER)
|
||||||
|
ADD_DEFINITIONS(-DQT_NO_CAST_TO_ASCII)
|
||||||
|
ADD_DEFINITIONS(-DQT_NO_CAST_FROM_ASCII)
|
||||||
|
ADD_DEFINITIONS(-DQT_NO_URL_CAST_FROM_STRING)
|
||||||
|
ADD_DEFINITIONS(-DQT_DEPRECATED_WARNINGS)
|
||||||
|
ADD_DEFINITIONS(-DQT_NO_CAST_FROM_BYTEARRAY)
|
||||||
|
#ADD_DEFINITIONS(-DQT_NO_DEPRECATED)
|
||||||
|
ADD_DEFINITIONS(-DQT_DISABLE_DEPRECATED_BEFORE=0x050900)
|
||||||
|
ADD_DEFINITIONS(-DQT_NO_FOREACH)
|
||||||
|
IF(NOT WIN32)
|
||||||
|
ADD_DEFINITIONS(-DQT_STRICT_ITERATORS)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ein paar NGL-spezifische Macros
|
||||||
|
#
|
||||||
|
INCLUDE(NGLMacros)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Projektspezifische Einstellungen?
|
||||||
|
#
|
||||||
|
NGL_INSTALL(SV AAR WS FILES lts-cc.conf DESTINATION . RENAME lts-cc.conf.distrib)
|
||||||
|
|
||||||
|
OPTION(ETS_MODE "Build for ETS (No B-Contamination etc)" OFF)
|
||||||
|
IF(ETS_MODE)
|
||||||
|
ADD_DEFINITIONS(-DETS_MODE)
|
||||||
|
ENDIF()
|
||||||
|
OPTION(TABLET_MODE "Build for TABLET " OFF)
|
||||||
|
IF(TABLET_MODE)
|
||||||
|
ADD_DEFINITIONS(-DTABLET_MODE)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
OPTION(GUEZ_MODE "Build for GUEZ " ON)
|
||||||
|
IF(GUEZ_MODE)
|
||||||
|
ADD_DEFINITIONS(-DGUEZ_MODE)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Doxygen
|
||||||
|
#
|
||||||
|
INCLUDE(DoxygenMacros)
|
||||||
|
IF(NOT DOXYGEN_OUT_DIR)
|
||||||
|
SET(DOXYGEN_OUT_DIR ${CMAKE_BINARY_DIR}/doxygen)
|
||||||
|
ENDIF()
|
||||||
|
ADD_DOXYGEN_TARGET(TDS ${CMAKE_SOURCE_DIR}/doxygen/Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile
|
||||||
|
${CMAKE_SOURCE_DIR}/doxygen/rheinmetall_defence_logo.jpg
|
||||||
|
${DOXYGEN_OUT_DIR})
|
||||||
|
|
||||||
|
IF(NOT DOXYGEN_OUT_DIR_2025)
|
||||||
|
SET(DOXYGEN_OUT_DIR_2025 ${CMAKE_BINARY_DIR}/doxygen2025)
|
||||||
|
ENDIF()
|
||||||
|
ADD_DOXYGEN_TARGET(TDS2025 ${CMAKE_SOURCE_DIR}/doxygen/Doxyfile2025.in ${CMAKE_BINARY_DIR}/Doxyfile
|
||||||
|
${CMAKE_SOURCE_DIR}/doxygen/rheinmetall_defence_logo.jpg
|
||||||
|
${DOXYGEN_OUT_DIR_2025})
|
||||||
|
|
||||||
|
#
|
||||||
|
# Compilerflags
|
||||||
|
#
|
||||||
|
IF(WIN32)
|
||||||
|
ADD_DEFINITIONS(-DWIN32 -D_USE_MATH_DEFINES -D_WIN32_WINNT=0x0501 -DUNICODE)
|
||||||
|
ELSE()
|
||||||
|
ADD_DEFINITIONS(-D_LINUX)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(MSVC)
|
||||||
|
# C4250: 'class1' : inherits 'class2::member' via dominance
|
||||||
|
# C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||||
|
# C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
|
||||||
|
# C4496: 'function': was declared deprecated
|
||||||
|
# C4267: 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||||
|
ADD_DEFINITIONS(-wd4250
|
||||||
|
-wd4251
|
||||||
|
-wd4290
|
||||||
|
-wd4496
|
||||||
|
-wd4267
|
||||||
|
-D_CRT_SECURE_NO_DEPRECATE
|
||||||
|
-D_CRT_NONSTDC_NO_DEPRECATE
|
||||||
|
-D_SCL_SECURE_NO_WARNINGS
|
||||||
|
-D_WINSOCK_DEPRECATED_NO_WARNINGS
|
||||||
|
/bigobj
|
||||||
|
)
|
||||||
|
# komplette Programmoptimierung
|
||||||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||||
|
# .pdb - Datei nicht gefunden
|
||||||
|
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /IGNORE:4099")
|
||||||
|
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /IGNORE:4099")
|
||||||
|
# min() und max() nicht als Macros
|
||||||
|
ADD_DEFINITIONS(-DNOMINMAX)
|
||||||
|
SET(CMAKE_MSVCIDE_RUN_PATH "" CACHE PATH "Run path to use when starting programs from MSVC")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(UNIX)
|
||||||
|
# Alle Warnungen aktivieren
|
||||||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wmissing-include-dirs")
|
||||||
|
# disable
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-overloaded-virtual")
|
||||||
|
IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0)
|
||||||
|
MESSAGE(FATAL_ERROR "gcc 4.8 or higher is required to build TDS: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||||
|
ENDIF()
|
||||||
|
IF(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9.99)
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-strict-aliasing")
|
||||||
|
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-attribute=const")
|
||||||
|
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-final-types")
|
||||||
|
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-final-methods")
|
||||||
|
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override")
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
OPTION(USE_IPO "Use interprocedural optimizations (cmake 3.9 or higher required)" OFF)
|
||||||
|
IF (USE_IPO AND CMAKE_VERSION VERSION_GREATER 3.8.9)
|
||||||
|
INCLUDE(CheckIPOSupported)
|
||||||
|
check_ipo_supported(RESULT result OUTPUT output)
|
||||||
|
IF(result)
|
||||||
|
SET(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
ELSE()
|
||||||
|
MESSAGE(WARNING "IPO is not supported: ${output}")
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
OPTION(USE_ASAN "Use Address Sanitizer (current Clang only)" OFF)
|
||||||
|
IF (USE_ASAN)
|
||||||
|
IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
MESSAGE(STATUS "enable Address Sanitizer with Clang (is: ${CMAKE_CXX_COMPILER_ID})")
|
||||||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1")
|
||||||
|
ELSE()
|
||||||
|
MESSAGE(STATUS "Address Sanitizer only supported with Clang (is: ${CMAKE_CXX_COMPILER_ID})")
|
||||||
|
ENDIF()
|
||||||
|
ENDIF(USE_ASAN)
|
||||||
|
|
||||||
|
OPTION(USE_UBSAN "Use UndefinedBehavior Sanitizer (current Clang only)" OFF)
|
||||||
|
IF (USE_UBSAN)
|
||||||
|
IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
|
||||||
|
ELSE()
|
||||||
|
MESSAGE(STATUS "UndefinedBehavior only supported with Clang (is: ${CMAKE_CXX_COMPILER_ID})")
|
||||||
|
ENDIF()
|
||||||
|
ENDIF(USE_UBSAN)
|
||||||
|
|
||||||
|
OPTION(BUILD_RPM "Build a RPM - install script in scripts subdir" OFF)
|
||||||
|
|
||||||
|
IF(BUILD_RPM)
|
||||||
|
ADD_SUBDIRECTORY(scripts)
|
||||||
|
ELSE()
|
||||||
|
ADD_DEFINITIONS(-DLTS_DEBUG)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF (NOT DEFINED _pname)
|
||||||
|
IF(GUEZ_MODE)
|
||||||
|
SET (_pname "guez")
|
||||||
|
ADD_DEFINITIONS(-DPROJECTBUILD="guez")
|
||||||
|
MESSAGE(STATUS "NO ProjectName givin! BUT GUEZ_MODE set PROJECTBUILD to guez")
|
||||||
|
ELSE()
|
||||||
|
MESSAGE(WARNING "NO ProjectName givin! Can't install coresponding lts-cc.conf! and PROJECTBUILD is empty")
|
||||||
|
ADD_DEFINITIONS(-DPROJECTBUILD="")
|
||||||
|
ENDIF()
|
||||||
|
ELSE()
|
||||||
|
MESSAGE(STATUS "ProjectName givin! PROJECTBUILD is ${_pname} ")
|
||||||
|
ADD_DEFINITIONS(-DPROJECTBUILD=\"${_pname}\")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# DDS benoetigt zwingend RTI
|
||||||
|
#
|
||||||
|
FIND_PACKAGE(RTI QUIET)
|
||||||
|
if(NOT RTI_FOUND)
|
||||||
|
message(WARNING "RTI not found; creating placeholder RTI::* imported targets to allow configure without RTI installation")
|
||||||
|
set(_rti_libs nddscore nddsc nddscpp2 rtiroutingservice)
|
||||||
|
foreach(_lib ${_rti_libs})
|
||||||
|
if(NOT TARGET RTI::${_lib})
|
||||||
|
add_library(RTI::${_lib} UNKNOWN IMPORTED)
|
||||||
|
set_property(TARGET RTI::${_lib} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Respect MIGRATE_MODULE: when set to a relative path (e.g. "Lts/Common"), only add
|
||||||
|
# the top-level subdirectories required to configure/build that module. This allows
|
||||||
|
# per-module migration builds via -DMIGRATE_MODULE=<path>.
|
||||||
|
if(NOT DEFINED MIGRATE_MODULE)
|
||||||
|
set(MIGRATE_MODULE "" CACHE STRING "If set, only add subdirectories covering this module (path relative to repo root)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(NGL_ADD_SUBDIR_GUARDED subdir)
|
||||||
|
if(MIGRATE_MODULE STREQUAL "")
|
||||||
|
add_subdirectory(${subdir})
|
||||||
|
else()
|
||||||
|
# normalize to forward slashes
|
||||||
|
string(REPLACE "\\" "/" _mig "${MIGRATE_MODULE}")
|
||||||
|
string(REPLACE "\\" "/" _sub "${subdir}")
|
||||||
|
# also compare basenames so MIGRATE_MODULE=Lts/DDS matches subdir 'DDS' when called from Lts
|
||||||
|
get_filename_component(_mig_base "${_mig}" NAME)
|
||||||
|
get_filename_component(_sub_base "${_sub}" NAME)
|
||||||
|
# If MIGRATE_MODULE either starts with the subdir, equals it, contains it as a path component,
|
||||||
|
# or the basename matches, include the subdir. This makes nested paths like
|
||||||
|
# 'Lts/Proxies/TrainingAreaBase' include both 'Lts' and its child 'Proxies'.
|
||||||
|
if(_mig MATCHES "^${_sub}(/|$)" OR _sub MATCHES "^${_mig}(/|$)" OR _mig MATCHES ".*/${_sub}(/|$)" OR _sub MATCHES ".*/${_mig}(/|$)" OR _mig_base STREQUAL _sub OR _sub_base STREQUAL _mig)
|
||||||
|
message(STATUS "Including subdir ${subdir} because MIGRATE_MODULE=${MIGRATE_MODULE}")
|
||||||
|
add_subdirectory(${subdir})
|
||||||
|
else()
|
||||||
|
message(STATUS "Skipping subdir ${subdir} because MIGRATE_MODULE=${MIGRATE_MODULE}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Add only guarded subdirectories
|
||||||
|
NGL_ADD_SUBDIR_GUARDED(TetraIF)
|
||||||
|
NGL_ADD_SUBDIR_GUARDED(Z/src)
|
||||||
|
NGL_ADD_SUBDIR_GUARDED(Lts)
|
||||||
|
# Special-case: when migrating TetraIF, also include TrainingAreaBase and Z so generated headers
|
||||||
|
# and internal logger target are available in the same build tree for TetraIF dependents.
|
||||||
|
if(DEFINED MIGRATE_MODULE AND MIGRATE_MODULE STREQUAL "TetraIF")
|
||||||
|
message(STATUS "Including helper modules for TetraIF migration: Lts/Proxies/TrainingAreaBase and Z/src")
|
||||||
|
add_subdirectory(Lts/Proxies/TrainingAreaBase)
|
||||||
|
add_subdirectory(Lts/Plugins/ProjectDataManagement)
|
||||||
|
add_subdirectory(Z/src)
|
||||||
|
endif()
|
||||||
|
# at last, to make sure we get all targets in NGL_TS_TARGETS property
|
||||||
|
NGL_ADD_SUBDIR_GUARDED(translations)
|
||||||
|
#-----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
OPTION(BUILD_QAV "Build script for local QAV analysis" OFF)
|
||||||
|
IF (BUILD_QAV)
|
||||||
|
ADD_SUBDIRECTORY(qav)
|
||||||
|
ENDIF()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Minimal CTestCustom.ctest placeholder
|
||||||
|
# Created to satisfy top-level CMake configure step
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#
|
||||||
|
# $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
|
||||||
|
set(LTS_SUBDIRS Tools Application Common DDS Plugins Projects Proxies Services Viewer)
|
||||||
|
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()
|
||||||
|
|
||||||
|
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
|
||||||
|
INCLUDE(MacroCodeGenIdl)
|
||||||
|
|
||||||
|
CODEGEN_WRITE_SERVICEFILE_INFO()
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
# $HeadURL$
|
||||||
|
# $Author$
|
||||||
|
#
|
||||||
|
|
||||||
|
ADD_SUBDIRECTORY(CodeGen)
|
||||||
|
ADD_SUBDIRECTORY(httq)
|
||||||
|
#ADD_SUBDIRECTORY(LtsBotan)
|
||||||
|
ADD_SUBDIRECTORY(LtsCommandLineParser)
|
||||||
|
ADD_SUBDIRECTORY(LtsCoordTrans)
|
||||||
|
ADD_SUBDIRECTORY(LtsCoordinate)
|
||||||
|
ADD_SUBDIRECTORY(LtsElevation)
|
||||||
|
ADD_SUBDIRECTORY(LtsLicence)
|
||||||
|
ADD_SUBDIRECTORY(LtsPluginManager)
|
||||||
|
ADD_SUBDIRECTORY(LtsSession)
|
||||||
|
ADD_SUBDIRECTORY(LtsUtils)
|
||||||
|
ADD_SUBDIRECTORY(LtsWidgets)
|
||||||
|
ADD_SUBDIRECTORY(LtsAppDescriptor)
|
||||||
|
ADD_SUBDIRECTORY(LtsAppManager)
|
||||||
|
ADD_SUBDIRECTORY(LtsMultiMonitor)
|
||||||
|
ADD_SUBDIRECTORY(LtsLogin)
|
||||||
|
ADD_SUBDIRECTORY(LtsNotification)
|
||||||
|
ADD_SUBDIRECTORY(LtsTimeline)
|
||||||
|
ADD_SUBDIRECTORY(LtsPrinting)
|
||||||
|
ADD_SUBDIRECTORY(LtsSerialPortQuerry)
|
||||||
|
ADD_SUBDIRECTORY(LtsScriptTimeline)
|
||||||
|
ADD_SUBDIRECTORY(LtsWebSocket)
|
||||||
|
ADD_SUBDIRECTORY(LtsProjectionWorkstation)
|
||||||
|
ADD_SUBDIRECTORY(LtsVirtualKeyboard)
|
||||||
|
ADD_SUBDIRECTORY(PluginInterfaces)
|
||||||
|
ADD_SUBDIRECTORY(ProjectConfig)
|
||||||
|
ADD_SUBDIRECTORY(TopicEditorBase) # TODO: rename / move ?
|
||||||
|
#ADD_SUBDIRECTORY(LtsEventTimeline)
|
||||||
|
#ADD_SUBDIRECTORY(LtsTranslator)
|
||||||
|
|
||||||
|
# Placeholder test scaffolding for incremental migration
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CommonUnitTestsPlaceholder/CMakeLists.txt")
|
||||||
|
add_subdirectory(CommonUnitTestsPlaceholder)
|
||||||
|
endif()
|
||||||
|
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
# $HeadURL$
|
||||||
|
# $Author$
|
||||||
|
#
|
||||||
|
|
||||||
|
FIND_PACKAGE(RTI)
|
||||||
|
FIND_PACKAGE(Grantlee5)
|
||||||
|
|
||||||
|
NGL_ADD_LIBRARY(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
|
||||||
|
Qt5::Xml
|
||||||
|
NGLLogger
|
||||||
|
ngltools
|
||||||
|
PUBLIC
|
||||||
|
Qt5::Core
|
||||||
|
)
|
||||||
|
NGL_TARGET_INCLUDE_DIRECTORIES(codegen_base
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_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>
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "RTI not found; building codegen_base without RTI include/compile definitions")
|
||||||
|
endif()
|
||||||
|
NGL_INSTALL_TARGET(codegen_base)
|
||||||
|
|
||||||
|
if (TARGET Grantlee5::Templates)
|
||||||
|
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
|
||||||
|
Grantlee5::Templates
|
||||||
|
NGLLogger
|
||||||
|
ProjectConfig
|
||||||
|
PUBLIC
|
||||||
|
codegen_base
|
||||||
|
)
|
||||||
|
NGL_TARGET_INCLUDE_DIRECTORIES(codegen_grantlee
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Grantlee5 not found; skipping codegen_grantlee target")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Smoke-test: parse all .grantlee templates in this directory to detect parse/render regressions
|
||||||
|
if (TARGET Grantlee5::Templates)
|
||||||
|
find_package(Qt6 COMPONENTS Core QUIET)
|
||||||
|
if (Qt6_FOUND)
|
||||||
|
set(QTCORE_TARGET Qt6::Core)
|
||||||
|
else()
|
||||||
|
find_package(Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
set(QTCORE_TARGET Qt5::Core)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(grantlee_parser_smoke tests/GrantleeParserSmoke.cpp)
|
||||||
|
target_link_libraries(grantlee_parser_smoke PRIVATE Grantlee5::Templates ${QTCORE_TARGET})
|
||||||
|
add_test(NAME GrantleeParserSmoke COMMAND grantlee_parser_smoke ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
NGL_ADD_EXECUTABLE(ALL codegen
|
||||||
|
main_grantlee.cpp
|
||||||
|
)
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(codegen
|
||||||
|
codegen_grantlee
|
||||||
|
LtsCommandLineParser
|
||||||
|
nglcoreapplication
|
||||||
|
Qt5::Core
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(codegen_gui
|
||||||
|
codegen_grantlee
|
||||||
|
LtsCommandLineParser
|
||||||
|
nglapplication
|
||||||
|
Qt5::Widgets
|
||||||
|
Grantlee5::Templates
|
||||||
|
)
|
||||||
+345
@@ -0,0 +1,345 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "CppTopicGeneratorGrantlee.h"
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <NGLLogger/INGLLogger.h>
|
||||||
|
#include <grantlee/context.h>
|
||||||
|
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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 QVector<QStringRef> sl = tti.name.splitRef(QStringLiteral("::"));
|
||||||
|
baseValueType = sl.at(0) + QLatin1String("::") + tti.baseValueType;
|
||||||
|
}
|
||||||
|
DEBUG_LOG(m_logger) << "topicTypeInformationByStructName(" << baseValueType << ")";
|
||||||
|
return m_structBuilder->topicTypeInformationByStructType(baseValueType);
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "GeneratorGrantleeBase.h"
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include "GrantleeWrappers.h"
|
||||||
|
#include "codegen_export.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "DatabaseGeneratorGrantlee.h"
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include <NGLLogger/INGLLogger.h>
|
||||||
|
#include <grantlee/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;
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "GeneratorGrantleeBase.h"
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include "GrantleeWrappers.h"
|
||||||
|
#include "codegen_export.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
+200
@@ -0,0 +1,200 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "GeneratorGrantleeBase.h"
|
||||||
|
#include "XmlReader.h"
|
||||||
|
#include "GrantleeWrappers.h"
|
||||||
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||||
|
#include <QFile>
|
||||||
|
#include <grantlee/context.h>
|
||||||
|
#include <grantlee/engine.h>
|
||||||
|
|
||||||
|
using namespace CodeGen;
|
||||||
|
GeneratorGrantleeBase::GeneratorGrantleeBase(const char *loggerName)
|
||||||
|
: m_engine(new Grantlee::Engine)
|
||||||
|
, m_ctx(new Grantlee::Context)
|
||||||
|
, m_structBuilder(nullptr)
|
||||||
|
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
QRegExp 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.midRef(idx, endIdx - idx).trimmed().toString();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "codegen_export.h"
|
||||||
|
#include "XmlReader.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
namespace NGL
|
||||||
|
{
|
||||||
|
class INGLLogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Grantlee
|
||||||
|
{
|
||||||
|
class Context;
|
||||||
|
class Engine;
|
||||||
|
class TemplateImpl;
|
||||||
|
using Template = QSharedPointer<TemplateImpl>;
|
||||||
|
}
|
||||||
|
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:
|
||||||
|
Grantlee::Engine *m_engine;
|
||||||
|
Grantlee::Context *m_ctx;
|
||||||
|
Grantlee::Template m_template;
|
||||||
|
StructBuilder *m_structBuilder;
|
||||||
|
NGL::INGLLogger &m_logger;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "GrantleeWrappers.h"
|
||||||
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||||
|
|
||||||
|
using namespace CodeGen;
|
||||||
|
|
||||||
|
QString IdlStructWrapper::extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity)
|
||||||
|
{
|
||||||
|
switch (extensiblity.underlying())
|
||||||
|
{
|
||||||
|
case dds::core::xtypes::ExtensibilityKind_def::FINAL:
|
||||||
|
return QStringLiteral("FINAL_EXTENSIBILITY");
|
||||||
|
case dds::core::xtypes::ExtensibilityKind_def::MUTABLE:
|
||||||
|
return QStringLiteral("MUTABLE_EXTENSIBILITY");
|
||||||
|
case dds::core::xtypes::ExtensibilityKind_def::EXTENSIBLE:
|
||||||
|
return QStringLiteral("EXTENSIBLE_EXTENSIBILITY");
|
||||||
|
}
|
||||||
|
Q_ASSERT_X(false, "IdlStructWrapper::extensibility2String", "Unknown extensiblity kind");
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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;");
|
||||||
|
|
||||||
|
if (!isOptional())
|
||||||
|
return QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") +
|
||||||
|
cppToJsonPre() + QStringLiteral("element.") + cppMemberName() + QStringLiteral("()") + cppToJsonPost();
|
||||||
|
|
||||||
|
return QStringLiteral("if (element.") + cppMemberName() + QStringLiteral("().is_set()) ") +
|
||||||
|
QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") +
|
||||||
|
cppToJsonPre() + QStringLiteral("element.") + cppMemberName() + QStringLiteral("().get()") + 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()) ");
|
||||||
|
|
||||||
|
ret += QStringLiteral("element.") + cppMemberName() + QStringLiteral("(") +
|
||||||
|
cppFromJsonPre2() + QStringLiteral("val") + cppFromJsonPost() + QStringLiteral(")");
|
||||||
|
|
||||||
|
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 : qAsConst(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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,643 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
|
||||||
|
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 : qAsConst(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)
|
||||||
|
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; }
|
||||||
|
|
||||||
|
|
||||||
|
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 ¬ification : qAsConst(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 { 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 : qAsConst(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;
|
||||||
|
static QString extensibility2String(dds::core::xtypes::ExtensibilityKind extensiblity);
|
||||||
|
};
|
||||||
|
|
||||||
|
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> ¶ms)
|
||||||
|
{
|
||||||
|
for (const auto ¶m : params)
|
||||||
|
m_requestParams.push_back(QVariant::fromValue(new IdlDataWrapper(param, this)));
|
||||||
|
}
|
||||||
|
QVariantList requestParams() const
|
||||||
|
{
|
||||||
|
return m_requestParams;
|
||||||
|
}
|
||||||
|
void addReplyParams(const QVector<IdlData> ¶ms)
|
||||||
|
{
|
||||||
|
for (const auto ¶m : 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> ¶ms)
|
||||||
|
{
|
||||||
|
for (const auto ¶m : 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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,943 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "SqlGenerator.h"
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include "XmlReader.h"
|
||||||
|
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QTextStream>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||||
|
|
||||||
|
using namespace CodeGen;
|
||||||
|
|
||||||
|
|
||||||
|
static QString getExerciseSchemaName(int exerciseId)
|
||||||
|
{
|
||||||
|
return QStringLiteral("ex_") + QString::number(exerciseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlGenerator::SqlGenerator(XmlReader *xmlReader, StructBuilder *structBuilder)
|
||||||
|
: m_xmlReader(xmlReader)
|
||||||
|
, m_structBuilder(structBuilder)
|
||||||
|
, m_createdByXmlFile(false)
|
||||||
|
, m_isValid(true)
|
||||||
|
, m_exerciseId(0)
|
||||||
|
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator"))
|
||||||
|
{}
|
||||||
|
|
||||||
|
SqlGenerator::SqlGenerator(const QString &xmlFile)
|
||||||
|
: m_xmlReader(new XmlReader)
|
||||||
|
, m_structBuilder(new StructBuilder(m_xmlReader))
|
||||||
|
, m_createdByXmlFile(true)
|
||||||
|
, m_isValid(false)
|
||||||
|
, m_exerciseId(0)
|
||||||
|
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CodeGen.SqlGenerator"))
|
||||||
|
{
|
||||||
|
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, 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> ¬ifications,
|
||||||
|
ExerciseDependency ed)
|
||||||
|
{
|
||||||
|
for (const DbNotifcationInformation ¬ification : 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 ¬ification,
|
||||||
|
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 ¬ification,
|
||||||
|
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 ¬ification) 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include "codegen_export.h"
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
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> ¬ifications, ExerciseDependency ed);
|
||||||
|
void generateDbNotification(const DbNotifcationInformation ¬ification, ExerciseDependency ed);
|
||||||
|
bool generateDbNotificationTrigger(const QString &tableName, const DbNotifcationInformation ¬ification, ExerciseDependency ed);
|
||||||
|
QString getNotificationFunctionName(const DbNotifcationInformation ¬ification) 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;
|
||||||
|
|
||||||
|
NGL::INGLLogger &m_logger;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,341 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include "XmlReader.h"
|
||||||
|
#include "codegen_export.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,927 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include "XmlReader.h"
|
||||||
|
|
||||||
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||||
|
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDomDocument>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <ngltools/ngltools_filesystem.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
QString errMsg;
|
||||||
|
int errLine = 0;
|
||||||
|
int errColumn = 0;
|
||||||
|
QDomDocument doc;
|
||||||
|
if (!doc.setContent(&f, &errMsg, &errLine, &errColumn))
|
||||||
|
{
|
||||||
|
m_errorString = QLatin1String("Error parsing service file '") + svcFile + QLatin1String("': ") +
|
||||||
|
errMsg + QLatin1String(" at ") + QString::number(errLine) +
|
||||||
|
QLatin1Char(':') + QString::number(errColumn);
|
||||||
|
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(','), QString::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 QVector<QStringRef> sl = isi.baseValueType.splitRef(QStringLiteral("::"));
|
||||||
|
if (sl.size() == 2)
|
||||||
|
loadExternalTopicType(sl.at(0).toString(), sl.at(1).toString());
|
||||||
|
|
||||||
|
const QString tmp = getAttribute(domIdlStruct, QStringLiteral("extensibility"), true);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 : qAsConst(m_functionTopics))
|
||||||
|
{
|
||||||
|
const QVector<QStringRef> sl1 = ft.requestTopicType.splitRef(QStringLiteral("::"));
|
||||||
|
if (sl1.size() == 2)
|
||||||
|
loadExternalTopicType(sl1.at(0).toString(), sl1.at(1).toString());
|
||||||
|
const QVector<QStringRef> sl2 = ft.replyTopicType.splitRef(QStringLiteral("::"));
|
||||||
|
if (sl2.size() == 2)
|
||||||
|
loadExternalTopicType(sl2.at(0).toString(), sl2.at(1).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 QVector<QStringRef> parts = fileName.splitRef(QLatin1Char('/'));
|
||||||
|
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.toString()] = fileName;
|
||||||
|
DEBUG_LOG(m_logger) << "xml: " << svcAbbreveation.toString() << " = " << fileName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto svcAbbreveation = fn.split(QLatin1Char('.')).at(0).toString().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 QVector<QStringRef> &parts)
|
||||||
|
{
|
||||||
|
if (parts.size() < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
const QString abbreviation = parts.last().split(QLatin1Char('.')).first().split(QLatin1Char('_')).first().toString();
|
||||||
|
const QString serviceName = parts.at(parts.size() - 2).toString();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
#include <QtCore/QSet>
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QVector>
|
||||||
|
|
||||||
|
#include <dds/core/xtypes/EnumType.hpp>
|
||||||
|
|
||||||
|
#include "codegen_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;
|
||||||
|
|
||||||
|
dds::core::xtypes::ExtensibilityKind extensibility = dds::core::xtypes::ExtensibilityKind::EXTENSIBLE;
|
||||||
|
|
||||||
|
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:
|
||||||
|
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 QVector<QStringRef> &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};
|
||||||
|
|
||||||
|
NGL::INGLLogger &m_logger;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
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:
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
#!/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 ):
|
||||||
|
outputHeader( outfile, parser, 'DTC' )
|
||||||
|
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('[\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 = 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(), parser.service_abbr + '_operator_rights.h')
|
||||||
|
generate(parser, OperatorRightsCpp(), parser.service_abbr + '_operator_rights.cpp')
|
||||||
|
else:
|
||||||
|
usage("invalid option '%s'" % option_str)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,172 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include "XmlReader.h"
|
||||||
|
#include "DaoGeneratorGrantlee.h"
|
||||||
|
#include "DatabaseGeneratorGrantlee.h"
|
||||||
|
#include "CppTopicGeneratorGrantlee.h"
|
||||||
|
#include "SqlGenerator.h"
|
||||||
|
|
||||||
|
#include <nglapplication/nglcoreapplication.h>
|
||||||
|
#include <QDir>
|
||||||
|
#include <NGLLogger/INGLLogger.h>
|
||||||
|
#include <LtsCommandLineParser/LtsCommandLineParser.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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "main_grantlee_gui.h"
|
||||||
|
#include "ui_main_grantlee_gui.h"
|
||||||
|
#include "StructBuilder.h"
|
||||||
|
#include "XmlReader.h"
|
||||||
|
#include "CppTopicGeneratorGrantlee.h"
|
||||||
|
#include "DatabaseGeneratorGrantlee.h"
|
||||||
|
|
||||||
|
#include <nglapplication/nglapplication.h>
|
||||||
|
#include <NGLLogger/INGLLogger.h>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <grantlee/engine.h>
|
||||||
|
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, m_xmlReader(nullptr)
|
||||||
|
, m_structBuilder(nullptr)
|
||||||
|
, m_generator(new CodeGen::CppTopicGeneratorGrantlee)
|
||||||
|
, m_engine(new Grantlee::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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
namespace CodeGen
|
||||||
|
{
|
||||||
|
class XmlReader;
|
||||||
|
class StructBuilder;
|
||||||
|
class GeneratorGrantleeBase;
|
||||||
|
}
|
||||||
|
namespace Grantlee
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
Grantlee::Engine *m_engine;
|
||||||
|
QString m_idlServiceInfoFile;
|
||||||
|
QString m_templateFile;
|
||||||
|
Ui_MainGrantleeGui *m_ui;
|
||||||
|
};
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<?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&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>&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>&Exit</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionOpen_Template">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Open Template</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSelect_Data_Source">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Select Data Source</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSave_Template">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sa&ve Template</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDatabaseGenerator">
|
||||||
|
<property name="text">
|
||||||
|
<string>&DatabaseGenerator</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDDS_Generator">
|
||||||
|
<property name="text">
|
||||||
|
<string>DDS &Generator</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
+732
@@ -0,0 +1,732 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 %}
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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>
|
||||||
|
|
||||||
|
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 %}
|
||||||
+304
@@ -0,0 +1,304 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 <DDS/DTC/generic_dtc_impl.h>
|
||||||
|
#include <dds/core/cond/StatusCondition.hpp>
|
||||||
|
#include <dds/core/cond/WaitSet.hpp>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
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 std::move(ret); // c++11 defect
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 %}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 <{{ include_prefix }}/{{ namespace_abbr }}_dtc.h>
|
||||||
|
#include <{{ include_prefix }}/{{ namespace_abbr }}_dtc_types.h>
|
||||||
|
#include <{{ include_prefix }}/logger.h>
|
||||||
|
|
||||||
|
#include <DDS/DDSConnector/dds_connector.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Interface
|
||||||
|
} // namespace {{ namespace }}
|
||||||
|
{% endautoescape %}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 %}
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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;
|
||||||
|
data.{{ dt.key.name }}({{ dt.key.name }});
|
||||||
|
return m_dts{{ dt.name }}->remove(data, errorMsgIfSampleDoesNotExists);
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %} {% endfor %}
|
||||||
|
|
||||||
|
} // namespace Interface
|
||||||
|
} // namespace {{ namespace }}
|
||||||
|
{% endautoescape %}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 %}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* @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 %}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 %}
|
||||||
+254
@@ -0,0 +1,254 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 std::move(ret); // c++11 defect
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 %}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 %}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 %}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* {{ 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 %}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* {{ namespace }} Function Topic Server implementation
|
||||||
|
* $Id$
|
||||||
|
* $HeadURL$
|
||||||
|
* $Author$
|
||||||
|
*/
|
||||||
|
|
||||||
|
{% autoescape off %}
|
||||||
|
// filename: {{ namespace_abbr }}_fts_impl.h
|
||||||
|
#pragma once
|
||||||
|
#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 %}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 %}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 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 %}
|
||||||
+876
@@ -0,0 +1,876 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T fromQVariant(const QVariant &val, type<T>);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
{% 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;
|
||||||
|
|
||||||
|
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 %}
|
||||||
+637
@@ -0,0 +1,637 @@
|
|||||||
|
/**
|
||||||
|
* 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 %}
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
// TODO: Problem with QT5_WRAP_CPP ...
|
||||||
|
#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 %}
|
||||||
|
|
||||||
|
LOGGER_T m_logger;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
{% endautoescape %}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* 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 %}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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>
|
||||||
|
// TODO: Problem with QT5_WRAP_CPP ...
|
||||||
|
#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 %}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @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.underlying())
|
||||||
|
{{% for idl_member in idl_enum.member %}
|
||||||
|
case {{ idl_enum.name }}_def::{{ idl_member.key }}: return QCoreApplication::translate("{{ idl_enum.name }}", "{{ idl_member.key }}");
|
||||||
|
{% endfor %}
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return QCoreApplication::translate("{{ idl_enum.name }}", "Unknown (%1)").arg(state.underlying());
|
||||||
|
}
|
||||||
|
{% 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 %}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* @RDE_HEADER@
|
||||||
|
* Convert DSS enumerations to translatable strings
|
||||||
|
* $Id$
|
||||||
|
* $HeadURL$
|
||||||
|
* $Author$
|
||||||
|
*/
|
||||||
|
|
||||||
|
{% autoescape off %}
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// filename: {{ namespace_abbr }}_translation.h
|
||||||
|
|
||||||
|
#include "{{ 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 %}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <grantlee/engine.h>
|
||||||
|
#include <grantlee/templateloader.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
QString templatesDir = QDir::currentPath();
|
||||||
|
if (argc > 1) {
|
||||||
|
templatesDir = QString::fromLocal8Bit(argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Grantlee::Engine engine;
|
||||||
|
auto loader = QSharedPointer<Grantlee::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) {
|
||||||
|
Grantlee::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() != Grantlee::NoError) {
|
||||||
|
qDebug() << "Parse error in" << f << ":" << t->errorString();
|
||||||
|
ok = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Render with empty context to ensure rendering path also works.
|
||||||
|
Grantlee::Context ctx;
|
||||||
|
QString out = t->render(&ctx);
|
||||||
|
if (out.isNull()) {
|
||||||
|
qDebug() << "Rendering produced null for" << f;
|
||||||
|
ok = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok ? 0 : 2;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Test)
|
||||||
|
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()
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#include "mocks/MockCoordTrans.h"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(CoordTransGTest, ForwardInverseRoundtrip) {
|
||||||
|
auto p = MockCoordTrans::forward(1.2345, 2.3456);
|
||||||
|
EXPECT_NEAR(p.first, 1.2345 * 111000.0, 1e-6);
|
||||||
|
EXPECT_NEAR(p.second, 2.3456 * 111000.0, 1e-6);
|
||||||
|
|
||||||
|
auto inv = MockCoordTrans::inverse(p.first, p.second);
|
||||||
|
EXPECT_NEAR(inv.first, 1.2345, 1e-9);
|
||||||
|
EXPECT_NEAR(inv.second, 2.3456, 1e-9);
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "mocks/MockCoordTrans.h"
|
||||||
|
|
||||||
|
class CoordTransSmoke : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void testForward() {
|
||||||
|
auto p = MockCoordTrans::forward(10.0, 20.0);
|
||||||
|
QVERIFY(qFuzzyCompare(p.first, 10.0 * 111000.0));
|
||||||
|
QVERIFY(qFuzzyCompare(p.second, 20.0 * 111000.0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_MAIN(CoordTransSmoke)
|
||||||
|
#include "coordTransTests_smoke.moc"
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
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)
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
// Minimal realistic mock for coordinate transforms used during migration.
|
||||||
|
// Forward conversion returns approximate meters using lat/lon*111000.
|
||||||
|
class MockCoordTrans {
|
||||||
|
public:
|
||||||
|
// approximate forward: (lat, lon) -> (x_m, y_m)
|
||||||
|
static std::pair<double,double> forward(double lat_deg, double lon_deg) {
|
||||||
|
return {lat_deg * 111000.0, lon_deg * 111000.0};
|
||||||
|
}
|
||||||
|
// approximate inverse
|
||||||
|
static std::pair<double,double> inverse(double x_m, double y_m) {
|
||||||
|
return {x_m / 111000.0, y_m / 111000.0};
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
# $HeadURL$
|
||||||
|
# $Author$
|
||||||
|
#
|
||||||
|
# Created by Mike Wille, SLSE, 2016-10-24
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# LtsAppDescriptor (lib)
|
||||||
|
SET(LtsAppDescriptor_SRCS
|
||||||
|
lts_app_descriptor.cpp
|
||||||
|
lts_app_descriptor.h
|
||||||
|
)
|
||||||
|
|
||||||
|
NGL_ADD_LIBRARY(WS LtsAppDescriptor SHARED ${LtsAppDescriptor_SRCS})
|
||||||
|
|
||||||
|
NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppDescriptor
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||||
|
)
|
||||||
|
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptor
|
||||||
|
PUBLIC
|
||||||
|
Qt5::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
|
||||||
|
Qt5::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_LIBRARY(WS NO_TRANSLATION LtsAppDescriptorEditor SHARED ${LtsAppDescriptorEditor_SRCS})
|
||||||
|
|
||||||
|
#NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppDescriptorEditor
|
||||||
|
# PUBLIC
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||||
|
#)
|
||||||
|
|
||||||
|
#NGL_TARGET_LINK_LIBRARIES(LtsAppDescriptorEditor
|
||||||
|
# PUBLIC
|
||||||
|
# Qt5::Core
|
||||||
|
# Qt5::Widgets
|
||||||
|
# LtsAppDescriptor
|
||||||
|
#)
|
||||||
|
|
||||||
|
#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)
|
||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
#include <LtsAppDescriptor/lts_app_descriptor.h>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfoList>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
|
||||||
|
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 : qAsConst(guiPluginFiles))
|
||||||
|
{
|
||||||
|
filenames += fi.absoluteFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QStringList guiPluginIIDs;
|
||||||
|
for (const QFileInfo &fi : qAsConst(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 : qAsConst(adv))
|
||||||
|
{
|
||||||
|
for (const LtsAppDescriptor::PluginInfo &pi : qAsConst(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+215
@@ -0,0 +1,215 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "lts_app_descriptor.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonParseError>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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.count(); 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#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,
|
||||||
|
|
||||||
|
Snapshot = 8,
|
||||||
|
SystemMonitor = 9,
|
||||||
|
Timeline = 10,
|
||||||
|
SymbolEditor = 11,
|
||||||
|
ScriptEditorEssentialsView = 12,
|
||||||
|
BaseDownloader = 13,
|
||||||
|
OnlineHelp = 14,
|
||||||
|
ThpSituationReplay = 15,
|
||||||
|
SituationTablet = 17,
|
||||||
|
SilhouetteEditor = 18,
|
||||||
|
Shootback = 19,
|
||||||
|
SAAs = 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PluginInfo
|
||||||
|
{
|
||||||
|
enum class PluginType
|
||||||
|
{
|
||||||
|
UNKNOWN,
|
||||||
|
DATA,
|
||||||
|
GUI,
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
LTSAPPDESCRIPTOR_EXPORT App appFromInteger(int i);
|
||||||
|
LTSAPPDESCRIPTOR_EXPORT AppDescriptionVector getAppDescriptionVector(const QByteArray &jsonDocument, QStringList &lastErrors,
|
||||||
|
const QStringList &ignoredPlugins, QString &loginPluginIid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+554
@@ -0,0 +1,554 @@
|
|||||||
|
#include "lts_app_descriptor_editor.h"
|
||||||
|
#include "ui_lts_app_descriptor_editor.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QStringListModel>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
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, SIGNAL(clicked(QModelIndex)), this, SLOT(onAppsClicked(QModelIndex)));
|
||||||
|
connect(m_ui->lvPlugins, SIGNAL(clicked(QModelIndex)), this, SLOT(onPluginsClicked(QModelIndex)));
|
||||||
|
connect(m_ui->lePluginType, SIGNAL(editingFinished()), this, SLOT(pluginTypeChanged()));
|
||||||
|
connect(m_ui->pbAddApp, SIGNAL(pressed()), this, SLOT(onAppAddClicked()));
|
||||||
|
connect(m_ui->pbAddPlugin, SIGNAL(pressed()), this, SLOT(onPluginAddClicked()));
|
||||||
|
connect(m_ui->pbDeleteApp, SIGNAL(pressed()), this, SLOT(onAppDeleteClicked()));
|
||||||
|
connect(m_ui->pbDeletePlugin, SIGNAL(pressed()), this, SLOT(onPluginDeletedClicked()));
|
||||||
|
connect(m_ui->pbUpdate, SIGNAL(pressed()), this, SLOT(onUpdateClicked()));
|
||||||
|
connect(m_ui->pbNewPlugin, SIGNAL(pressed()), this, SLOT(onNewpluginClicked()));
|
||||||
|
connect(m_ui->pbNewApp, SIGNAL(pressed()), this, SLOT(onNewAppClicked()));
|
||||||
|
connect(m_ui->lePluginName, SIGNAL(textChanged(QString)), this, SLOT(enableNewPlugin()));
|
||||||
|
connect(m_ui->lePluginType, SIGNAL(textChanged(QString)), this, SLOT(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();
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtWidgets/QWidget>
|
||||||
|
#include <QModelIndex>
|
||||||
|
|
||||||
|
#include "lts_app_descriptor.h"
|
||||||
|
|
||||||
|
#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();
|
||||||
|
};
|
||||||
+434
@@ -0,0 +1,434 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "lts_app_descriptor_editor.h"
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <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;
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "ui_app.h"
|
||||||
|
#include "AppChooser/app_chooser_model.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
<?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"><App Name></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"><html><head/><body><p>&lt;Description with wordwrap&gt;</p></body></html></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>
|
||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include "AppChooser/app_chooser.h"
|
||||||
|
#include "ui_app_chooser.h"
|
||||||
|
#include "AppChooser/app_chooser_model.h"
|
||||||
|
#include "lts_app_manager.h"
|
||||||
|
#include "AppChooser/CustomViews/app.h"
|
||||||
|
|
||||||
|
#include <QCloseEvent>
|
||||||
|
|
||||||
|
#include <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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <LtsAppDescriptor/lts_app_descriptor.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
<?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>
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <LtsAppDescriptor/lts_app_descriptor.h>
|
||||||
|
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
# $HeadURL$
|
||||||
|
# $Author$
|
||||||
|
# Created by Mike Wille, SLSE, 2016-10-27
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# LtsAppManager
|
||||||
|
|
||||||
|
set(LtsAppManager_SRCS
|
||||||
|
lts_app_manager.cpp
|
||||||
|
AppChooser/app_chooser.cpp
|
||||||
|
AppChooser/app_chooser_model.cpp
|
||||||
|
AppChooser/CustomViews/app.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
NGL_WRAP_UI(LtsAppManager_SRCS
|
||||||
|
AppChooser/app_chooser.ui
|
||||||
|
AppChooser/CustomViews/app.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
NGL_ADD_LIBRARY(WS LtsAppManager SHARED ${LtsAppManager_SRCS})
|
||||||
|
NGL_TARGET_INCLUDE_DIRECTORIES(LtsAppManager
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/AppChooser/
|
||||||
|
)
|
||||||
|
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(LtsAppManager
|
||||||
|
PRIVATE
|
||||||
|
NGLLogger
|
||||||
|
LtsDataView
|
||||||
|
LtsWidgets
|
||||||
|
LtsLogin
|
||||||
|
LtsPluginManager
|
||||||
|
LtsSession
|
||||||
|
LtsSessionData
|
||||||
|
LtsUtilsSettings
|
||||||
|
LtsMultiMonitor
|
||||||
|
ProjectConfig
|
||||||
|
Qt5::Gui
|
||||||
|
Qt5::Widgets
|
||||||
|
Qt5::Network # debug server
|
||||||
|
OpmSettingsGuiPluginInterface
|
||||||
|
LtsPluginLoader
|
||||||
|
PUBLIC
|
||||||
|
Qt5::Core
|
||||||
|
LtsAppDescriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
NGL_INSTALL_TARGET(LtsAppManager)
|
||||||
+1151
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,123 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LtsAppDescriptor/lts_app_descriptor.h>
|
||||||
|
#include <Common/ProjectConfig/project_defines.h>
|
||||||
|
#include <LtsWidgets/lts_dialog.h>
|
||||||
|
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||||
|
#include <LtsSession/lts_session.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LTSAPPMANAGER_EXPORT
|
||||||
|
# ifdef BUILD_STATIC_LIBRARIES
|
||||||
|
# define LTSAPPMANAGER_EXPORT
|
||||||
|
# else
|
||||||
|
# if defined(MAKE_LTSAPPMANAGER_LIB)
|
||||||
|
# define LTSAPPMANAGER_EXPORT Q_DECL_EXPORT
|
||||||
|
# else
|
||||||
|
# define LTSAPPMANAGER_EXPORT Q_DECL_IMPORT
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace LtsPluginManager
|
||||||
|
{
|
||||||
|
class LtsPluginManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace LtsLogin
|
||||||
|
{
|
||||||
|
class LtsLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace LtsSession
|
||||||
|
{
|
||||||
|
class LtsSession;
|
||||||
|
struct DdsConnectionParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace DeveloperTools
|
||||||
|
{
|
||||||
|
class DevDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
|
||||||
|
namespace LtsAppManager
|
||||||
|
{
|
||||||
|
|
||||||
|
class LTSAPPMANAGER_EXPORT LtsAppManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static LtsAppManager* getInstance();
|
||||||
|
|
||||||
|
void showLogin();
|
||||||
|
|
||||||
|
|
||||||
|
LtsPluginManager::LtsPluginManager *createApp(LtsAppDescriptor::App appId, const LtsSession::DdsConnectionParams &connParams,
|
||||||
|
const QString &workspaceId = QString(), bool hidden = false, bool force = false);
|
||||||
|
bool startAppOrLetUserChoose(LtsAppDescriptor::App appId, const LtsSession::DdsConnectionParams &connParams, bool force = false);
|
||||||
|
void setLoginPluginIid(const QString &loginPluginIid) { m_loginPluginIid = loginPluginIid; }
|
||||||
|
void setMainApp(LtsAppDescriptor::App appId = LtsAppDescriptor::App::Login);
|
||||||
|
QVector<LtsPluginManager::LtsPluginManager *> getAppList() const { return m_appList ;}
|
||||||
|
LtsAppDescriptor::AppDescriptionVector getAvailableApps() const { return m_availableApps; }
|
||||||
|
void setAvailableApps(const LtsAppDescriptor::AppDescriptionVector &availableApps);
|
||||||
|
bool hasRightForApp(const LtsAppDescriptor::AppDescription &appDescription) const;
|
||||||
|
LtsAppDescriptor::AppDescription getApp(LtsAppDescriptor::App appId) const;
|
||||||
|
bool hasApp(LtsAppDescriptor::App appId) const;
|
||||||
|
int getNumberOfSituationMapInstances() const;
|
||||||
|
void debugSendMessage(const QString &msg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool otherSessionsResponsibility(LtsSession::LtsSession *thisSession, const QVector<LtsAppDescriptor::App> &requiredAppTypes,
|
||||||
|
const QVector<IDL::PartitionType> &requiredPartitionTypes, const QString &menuGroup,
|
||||||
|
const QString &identifier, LtsWidgets::LayoutArea area);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct LtsAppManagerHandler;
|
||||||
|
friend class DeveloperTools::DevDialog;
|
||||||
|
|
||||||
|
LtsAppManager();
|
||||||
|
~LtsAppManager();
|
||||||
|
|
||||||
|
bool startAppOrLetUserChoose(const LtsAppDescriptor::AppDescription &appDescription, const LtsSession::DdsConnectionParams &connParams, bool force = false);
|
||||||
|
LtsPluginManager::LtsPluginManager *createApp(const LtsAppDescriptor::AppDescription &appDescription,
|
||||||
|
const LtsSession::DdsConnectionParams &connParams,
|
||||||
|
const QString &workspaceId = QString(),
|
||||||
|
bool hidden = false, bool force = false);
|
||||||
|
void saveState();
|
||||||
|
|
||||||
|
LtsAppDescriptor::AppDescriptionVector m_availableApps;
|
||||||
|
QVector<LtsPluginManager::LtsPluginManager *> m_appList;
|
||||||
|
LtsLogin::LtsLogin *m_login;
|
||||||
|
LtsAppDescriptor::AppDescription m_app;
|
||||||
|
QString m_loginPluginIid;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotLogout();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotQuit();
|
||||||
|
void slotSoftQuit();
|
||||||
|
void slotShowLogin();
|
||||||
|
void slotAppDestroyed();
|
||||||
|
void slotStartDebugServer();
|
||||||
|
void hideAllOtherBLWs(QObject *_caller);
|
||||||
|
void taskSwitch(QObject *_caller);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void signalLoginDone();
|
||||||
|
void signalLogoffDone();
|
||||||
|
void signalShowLogin();
|
||||||
|
void signalInitializationFinished();
|
||||||
|
void signalCommand(QTcpSocket *client, const QStringList argList);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
find_package(Botan2)
|
||||||
|
|
||||||
|
if (BOTAN_FOUND AND CMAKE_VERSION VERSION_GREATER 3.15.9) # botan doesn't use cmake, make it a cmake target | imported targets don't work correctly with 3.10
|
||||||
|
add_library(LtsBotan STATIC IMPORTED GLOBAL)
|
||||||
|
set_target_properties(LtsBotan PROPERTIES IMPORTED_LOCATION ${BOTAN_LIBRARIES})
|
||||||
|
set_target_properties(LtsBotan PROPERTIES INCLUDE_DIRECTORIES ${BOTAN_INCLUDE_DIRS})
|
||||||
|
target_compile_definitions(LtsBotan INTERFACE BOTAN_FOUND)
|
||||||
|
target_include_directories(LtsBotan SYSTEM INTERFACE ${BOTAN_INCLUDE_DIRS})
|
||||||
|
set(LTS_BOTAN LtsBotan)
|
||||||
|
|
||||||
|
message(STATUS "BOTAN_INCLUDE_DIRS = ${BOTAN_INCLUDE_DIRS}, BOTAN_LIBRARIES = ${BOTAN_LIBRARIES}, LTS_BOTAN = ${LTS_BOTAN}")
|
||||||
|
endif()
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
# $HeadURL$
|
||||||
|
# $Author$
|
||||||
|
#
|
||||||
|
|
||||||
|
NGL_ADD_LIBRARY2(ALL LtsCommandLineParser SHARED
|
||||||
|
LtsCommandLineParser.cpp
|
||||||
|
LtsCommandLineParser.h
|
||||||
|
)
|
||||||
|
NGL_TARGET_INCLUDE_DIRECTORIES(LtsCommandLineParser
|
||||||
|
INTERFACE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||||
|
)
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(LtsCommandLineParser
|
||||||
|
PRIVATE
|
||||||
|
ngltools
|
||||||
|
Qt5::Network
|
||||||
|
PUBLIC
|
||||||
|
Qt5::Core
|
||||||
|
)
|
||||||
+183
@@ -0,0 +1,183 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "LtsCommandLineParser.h"
|
||||||
|
#include <ngltools/ngltools_filesystem.h>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
#include <QFlags>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace LtsCommandLineParser
|
||||||
|
{
|
||||||
|
static constexpr int s_maxDomainId = 232;
|
||||||
|
|
||||||
|
LtsCommandLineParser::LtsCommandLineParser(ProvideCommandLineParameterOptions opts)
|
||||||
|
: QCommandLineParser()
|
||||||
|
, m_enabledOptions(opts)
|
||||||
|
{
|
||||||
|
if (NGL::isAppInBuildBasePath() || m_enabledOptions.testFlag(CreateDomainIdFromIp))
|
||||||
|
{
|
||||||
|
const QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
|
||||||
|
for (const QHostAddress &address : addresses)
|
||||||
|
{
|
||||||
|
if (address.isLoopback() || address.isNull())
|
||||||
|
continue;
|
||||||
|
if (address.protocol() != QAbstractSocket::IPv4Protocol)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
m_generatedDomainId = (address.toIPv4Address() & 0x000000ff) % 232;
|
||||||
|
if (m_generatedDomainId == 0)
|
||||||
|
m_generatedDomainId = s_maxDomainId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_enabledOptions.testFlag(ExerciseId))
|
||||||
|
addOption({{QStringLiteral("e"), QStringLiteral("exerciseid")},
|
||||||
|
QStringLiteral("Default: -1 (invalid, get from lts-cc.conf)"),
|
||||||
|
QStringLiteral("exerciseId"),
|
||||||
|
QStringLiteral("-1")
|
||||||
|
});
|
||||||
|
if (m_enabledOptions.testFlag(DomainId))
|
||||||
|
addOption({{QStringLiteral("d"), QStringLiteral("domainid")},
|
||||||
|
QStringLiteral("Default: ") + QString::number(m_generatedDomainId),
|
||||||
|
QStringLiteral("domainId"),
|
||||||
|
QString::number(m_generatedDomainId)
|
||||||
|
});
|
||||||
|
if (m_enabledOptions.testFlag(CustomConfigFile))
|
||||||
|
{
|
||||||
|
const QString cf(NGL::findProjectConfigFile());
|
||||||
|
addOption({{QStringLiteral("i"), QStringLiteral("ini")},
|
||||||
|
QStringLiteral("Default: ") + cf,
|
||||||
|
QStringLiteral("filename"),
|
||||||
|
cf
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (m_enabledOptions.testFlag(TcpConnectionUrl))
|
||||||
|
{
|
||||||
|
const QString defaultTcpUrl(QStringLiteral("EXCTDS01:7400"));
|
||||||
|
addOption({{QStringLiteral("t"), QStringLiteral("tcpurl")},
|
||||||
|
QStringLiteral("Default: ") + defaultTcpUrl,
|
||||||
|
QStringLiteral("hostname:port"),
|
||||||
|
defaultTcpUrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
addHelpOption();
|
||||||
|
|
||||||
|
if (!m_enabledOptions.testFlag(NoVersion))
|
||||||
|
addVersionOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
int LtsCommandLineParser::domainId() const
|
||||||
|
{
|
||||||
|
if (m_domainId == 0 && m_generatedDomainId != 0 && !m_domainIdSetByFunction)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
fprintf(stdout, "\n\nAttention: using a default domain id != 0 because executable is run inside builddir - default domainid is %d\n\n", m_generatedDomainId);
|
||||||
|
#else
|
||||||
|
fprintf(stdout, "\n\n\e[31mAttention: using a default domain id != 0 because executable is run inside builddir - default domainid is %d\e[0m\n\n", m_generatedDomainId);
|
||||||
|
#endif
|
||||||
|
return m_generatedDomainId;
|
||||||
|
}
|
||||||
|
return m_domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCommandLineParser::setDomainId(int id)
|
||||||
|
{
|
||||||
|
m_domainIdSetByFunction = true;
|
||||||
|
m_domainId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCommandLineParser::iniFileSet() const
|
||||||
|
{
|
||||||
|
if (!m_enabledOptions.testFlag(CustomConfigFile))
|
||||||
|
return false;
|
||||||
|
return isSet(QStringLiteral("i"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCommandLineParser::useTcpConnection() const
|
||||||
|
{
|
||||||
|
if (!m_enabledOptions.testFlag(TcpConnectionUrl))
|
||||||
|
return false;
|
||||||
|
return isSet(QStringLiteral("t"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl LtsCommandLineParser::tcpConnectionUrl() const
|
||||||
|
{
|
||||||
|
if (!m_enabledOptions.testFlag(TcpConnectionUrl))
|
||||||
|
return QUrl();
|
||||||
|
if (!useTcpConnection())
|
||||||
|
return QUrl();
|
||||||
|
QString str = value(QStringLiteral("t"));
|
||||||
|
|
||||||
|
str.replace(QLatin1String("tcpv4_lan"), QLatin1String("tcpv4lan"));
|
||||||
|
QUrl url = QUrl::fromUserInput(str);
|
||||||
|
if (url.scheme() == QLatin1String("http") ||
|
||||||
|
url.scheme() == QLatin1String("file"))
|
||||||
|
url.setScheme(QStringLiteral("tcpv4lan"));
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCommandLineParser::init(bool bParseHelp)
|
||||||
|
{
|
||||||
|
if (!parse(QCoreApplication::arguments()))
|
||||||
|
{
|
||||||
|
fprintf(stdout, "\nBad option: %s \n", qPrintable(errorText()));
|
||||||
|
if (!m_unitModuleTestMode)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_enabledOptions.testFlag(ExerciseId))
|
||||||
|
m_exerciseId = value(QStringLiteral("e")).toInt();
|
||||||
|
if (m_enabledOptions.testFlag(DomainId))
|
||||||
|
m_domainId = value(QStringLiteral("d")).toInt();
|
||||||
|
if (m_enabledOptions.testFlag(CustomConfigFile))
|
||||||
|
m_iniFile = value(QStringLiteral("i"));
|
||||||
|
|
||||||
|
if (bParseHelp && isSet(QStringLiteral("h")))
|
||||||
|
showHelp();
|
||||||
|
if (!m_unitModuleTestMode && isSet(QStringLiteral("v")))
|
||||||
|
showVersion();
|
||||||
|
if (!domainIdProvided())
|
||||||
|
{
|
||||||
|
const QString iniFile = m_iniFile.isEmpty() ? NGL::findProjectConfigFile() : m_iniFile;
|
||||||
|
if (!iniFile.isEmpty())
|
||||||
|
{
|
||||||
|
QSettings s(iniFile, QSettings::IniFormat);
|
||||||
|
bool bOk;
|
||||||
|
int domainId = s.value(QStringLiteral("Global/DomainId"), m_domainId).toInt(&bOk);
|
||||||
|
if (bOk && domainId > 0 && domainId <= s_maxDomainId)
|
||||||
|
m_domainId = domainId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCommandLineParser::showHelp(int exitCode)
|
||||||
|
{
|
||||||
|
if (!m_additionalHelpStr.isEmpty())
|
||||||
|
fprintf(stdout, "%s", qPrintable(m_additionalHelpStr));
|
||||||
|
|
||||||
|
|
||||||
|
QCommandLineParser::showHelp(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCommandLineParser::domainIdProvided() const
|
||||||
|
{
|
||||||
|
if (!m_enabledOptions.testFlag(DomainId))
|
||||||
|
return false;
|
||||||
|
return isSet(QStringLiteral("d"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCommandLineParser::exerciseIdProvided() const
|
||||||
|
{
|
||||||
|
if (!m_enabledOptions.testFlag(ExerciseId))
|
||||||
|
return false;
|
||||||
|
return isSet(QStringLiteral("e"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <LtsCommandLineParser/ltscommandlineparser_export.h>
|
||||||
|
|
||||||
|
class QUrl;
|
||||||
|
namespace LtsCommandLineParser
|
||||||
|
{
|
||||||
|
class LTSCOMMANDLINEPARSER_EXPORT LtsCommandLineParser : public QCommandLineParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ProvideCommandLineParameterOptions
|
||||||
|
{
|
||||||
|
None = 0x0000,
|
||||||
|
CreateDomainIdFromIp = 0x0001,
|
||||||
|
DomainId = 0x0002,
|
||||||
|
ExerciseId = 0x0004,
|
||||||
|
CustomConfigFile = 0x0008,
|
||||||
|
TcpConnectionUrl = 0x0010,
|
||||||
|
NoVersion = 0x0020
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit LtsCommandLineParser(ProvideCommandLineParameterOptions opts = ProvideCommandLineParameterOptions(CreateDomainIdFromIp|DomainId|ExerciseId|CustomConfigFile));
|
||||||
|
virtual ~LtsCommandLineParser() = default;
|
||||||
|
int domainId() const;
|
||||||
|
void setDomainId(int id);
|
||||||
|
int exerciseId() const { return m_exerciseId; }
|
||||||
|
QString iniFile() const { return m_iniFile; }
|
||||||
|
bool iniFileSet() const;
|
||||||
|
bool useTcpConnection() const;
|
||||||
|
QUrl tcpConnectionUrl() const;
|
||||||
|
virtual bool init(bool bParseHelp = true);
|
||||||
|
|
||||||
|
void showHelp(int exitCode = 0);
|
||||||
|
bool domainIdProvided() const;
|
||||||
|
bool exerciseIdProvided() const;
|
||||||
|
|
||||||
|
void setUnitModuleTestMode(bool onOff) { m_unitModuleTestMode = onOff; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_additionalHelpStr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFlags<ProvideCommandLineParameterOptions> m_enabledOptions;
|
||||||
|
int m_exerciseId{0};
|
||||||
|
int m_domainId{0};
|
||||||
|
int m_generatedDomainId{0};
|
||||||
|
bool m_domainIdSetByFunction{false};
|
||||||
|
bool m_unitModuleTestMode{false};
|
||||||
|
QString m_iniFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
# $HeadURL$
|
||||||
|
# $Author$
|
||||||
|
# Created by Christian Ehrlicher, SLSE, 2016-08-31
|
||||||
|
#
|
||||||
|
|
||||||
|
FIND_PACKAGE(GeographicLib)
|
||||||
|
NGL_ADD_LIBRARY2(LtsCoordTrans WS AAR SV HLA SHARED
|
||||||
|
lts_coord_trans.h
|
||||||
|
lts_coord_trans.cpp
|
||||||
|
)
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(LtsCoordTrans
|
||||||
|
PRIVATE
|
||||||
|
Qt5::Core
|
||||||
|
)
|
||||||
|
if (GeographicLib_FOUND)
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(LtsCoordTrans
|
||||||
|
PRIVATE GeographicLib
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "GeographicLib not found; building LtsCoordTrans without it")
|
||||||
|
endif()
|
||||||
|
NGL_TARGET_INCLUDE_DIRECTORIES(LtsCoordTrans
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(ltsCoordWidget_SRCS
|
||||||
|
lts_coord_widget.cpp
|
||||||
|
lts_coord_widget.h
|
||||||
|
lts_coord_widget_p.cpp
|
||||||
|
lts_coord_widget_p.h
|
||||||
|
)
|
||||||
|
NGL_WRAP_UI(ltsCoordWidget_SRCS
|
||||||
|
lts_coord_widget_mgrs.ui
|
||||||
|
lts_coord_widget_wgs84.ui
|
||||||
|
)
|
||||||
|
NGL_ADD_LIBRARY2(ltsCoordWidget WS AAR SV SHARED ${ltsCoordWidget_SRCS})
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(ltsCoordWidget
|
||||||
|
PRIVATE
|
||||||
|
LtsCoordTrans
|
||||||
|
PUBLIC
|
||||||
|
Qt5::Core
|
||||||
|
Qt5::Widgets
|
||||||
|
)
|
||||||
|
NGL_TARGET_INCLUDE_DIRECTORIES(ltsCoordWidget
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||||
|
)
|
||||||
|
|
||||||
|
IF(TARGET LtsCoordTrans)
|
||||||
|
SET(_tools lonlat2mgrs mgrs2lonlat)
|
||||||
|
FOREACH(_tool ${_tools})
|
||||||
|
NGL_ADD_EXECUTABLE(ALL ${_tool} ${_tool}.cpp)
|
||||||
|
NGL_TARGET_LINK_LIBRARIES(${_tool}
|
||||||
|
Qt5::Core
|
||||||
|
GeographicLib
|
||||||
|
)
|
||||||
|
NGL_INSTALL_TARGET(${_tool})
|
||||||
|
ENDFOREACH()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(BUILD_TESTING AND TARGET ltsCoordWidget)
|
||||||
|
ADD_EXECUTABLE(lts_coord_widget_test lts_coord_widget_test.cpp)
|
||||||
|
TARGET_LINK_LIBRARIES(lts_coord_widget_test
|
||||||
|
ltsCoordWidget
|
||||||
|
LtsCoordTrans
|
||||||
|
)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(BUILD_TESTING AND TARGET LtsCoordTrans)
|
||||||
|
ADD_EXECUTABLE(lts_coord_trans_unit_test
|
||||||
|
lts_coord_trans_unit_test.cpp
|
||||||
|
lts_coord_trans_unit_test.h
|
||||||
|
)
|
||||||
|
TARGET_LINK_LIBRARIES(lts_coord_trans_unit_test
|
||||||
|
LtsCoordTrans
|
||||||
|
Qt5::Test
|
||||||
|
GeographicLib
|
||||||
|
)
|
||||||
|
ADD_TEST(NAME lts_coord_trans_unit_test COMMAND $<TARGET_FILE:lts_coord_trans_unit_test>)
|
||||||
|
ENDIF()
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <GeographicLib/UTMUPS.hpp>
|
||||||
|
#include <GeographicLib/MGRS.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 3 || argc > 5)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <lon_degree> <lat_degree> [<precision>]\n", argv[0]);
|
||||||
|
fprintf(stderr, "Usage: %s <lon_rad> <lat_rad> -r [<precision>]\n", argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lonLatInRad = false;
|
||||||
|
double lon = atof(argv[1]);
|
||||||
|
double lat = atof(argv[2]);
|
||||||
|
int prec = 5;
|
||||||
|
if (argc >= 4)
|
||||||
|
{
|
||||||
|
int precArgId = -1;
|
||||||
|
if (strcmp(argv[3], "-r") == 0)
|
||||||
|
{
|
||||||
|
lonLatInRad = true;
|
||||||
|
if (argc == 5)
|
||||||
|
precArgId = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
precArgId = 3;
|
||||||
|
if (precArgId > 0)
|
||||||
|
prec = atoi(argv[precArgId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mgrs;
|
||||||
|
|
||||||
|
int zone;
|
||||||
|
bool northp;
|
||||||
|
double x, y;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const double latDeg = lonLatInRad ? lat * 180 / M_PI : lat;
|
||||||
|
const double lonDeg = lonLatInRad ? lon * 180 / M_PI : lon;
|
||||||
|
GeographicLib::UTMUPS::Forward(latDeg, lonDeg, zone, northp, x, y);
|
||||||
|
GeographicLib::MGRS::Forward(zone, northp, x, y, prec, mgrs);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error converting %9.6f, %9.6f to MGRS coords\n", lat, lon);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
std::string part1, part2, part3, part4;
|
||||||
|
part1 = mgrs.substr(0, 3);
|
||||||
|
part2 = mgrs.substr(3, 2);
|
||||||
|
if (prec > 0)
|
||||||
|
{
|
||||||
|
part3 = mgrs.substr(5, prec);
|
||||||
|
part4 = mgrs.substr(5 + prec, prec);
|
||||||
|
}
|
||||||
|
fprintf(stdout, "%13.10f, %13.10f = %s %s %s %s (UTM: %d %.2f, %.2f %c)\n",
|
||||||
|
lon, lat,
|
||||||
|
part1.c_str(), part2.c_str(), part3.c_str(), part4.c_str(),
|
||||||
|
zone, x, y, northp ? 'N' : 'S');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+445
@@ -0,0 +1,445 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "lts_coord_trans.h"
|
||||||
|
|
||||||
|
#include <GeographicLib/Geocentric.hpp>
|
||||||
|
#include <GeographicLib/UTMUPS.hpp>
|
||||||
|
#include <GeographicLib/MGRS.hpp>
|
||||||
|
#include <QPointF>
|
||||||
|
#include <QRectF>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
namespace LtsCoordTrans
|
||||||
|
{
|
||||||
|
|
||||||
|
static long s_conversion_counter_MGRS_Forward = 0;
|
||||||
|
static long s_conversion_counter_MGRS_Reverse = 0;
|
||||||
|
|
||||||
|
static long s_conversion_counter_WGS84_Forward = 0;
|
||||||
|
static long s_conversion_counter_WGS84_Reverse = 0;
|
||||||
|
|
||||||
|
static long s_conversion_counter_UTMUPS_Forward = 0;
|
||||||
|
static long s_conversion_counter_UTMUPS_Reverse = 0;
|
||||||
|
|
||||||
|
|
||||||
|
inline void incrementCounter(long &counterRef)
|
||||||
|
{
|
||||||
|
#ifdef LTS_DEBUG
|
||||||
|
++counterRef;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline std::string cleanupMgrsString(const QString &in)
|
||||||
|
{
|
||||||
|
const QByteArray ba(in.toLatin1());
|
||||||
|
std::string ret;
|
||||||
|
ret.reserve(ba.size());
|
||||||
|
for (const char c : ba)
|
||||||
|
{
|
||||||
|
if (c != ' ')
|
||||||
|
ret += c;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString UtmCoordinate::mgrs(int prec, bool withSpaces) const
|
||||||
|
{
|
||||||
|
if (prec < 0)
|
||||||
|
prec = 0;
|
||||||
|
if (prec > 11)
|
||||||
|
prec = 11;
|
||||||
|
|
||||||
|
std::string mgrs;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GeographicLib::MGRS::Forward(zone, northp, x, y, prec, mgrs);
|
||||||
|
incrementCounter(s_conversion_counter_MGRS_Forward);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error while calculating mgrs from utm %d, %9.6f, %9.6f: %s\n", zone, x, y, e.what());
|
||||||
|
mgrs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ret;
|
||||||
|
if (withSpaces && mgrs.size() == static_cast<size_t>(5 + 2 * prec))
|
||||||
|
{
|
||||||
|
|
||||||
|
const char *ptr = mgrs.c_str();
|
||||||
|
|
||||||
|
ret = QString::fromLatin1(ptr, 3) + QLatin1Char(' ') +
|
||||||
|
QString::fromLatin1(ptr + 3, 2);
|
||||||
|
if (prec > 0)
|
||||||
|
{
|
||||||
|
ret += QLatin1Char(' ') + QString::fromLatin1(ptr + 5, prec) +
|
||||||
|
QLatin1Char(' ') + QString::fromLatin1(ptr + 5 + prec, prec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = QString::fromLatin1(mgrs.c_str(), mgrs.size());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF UtmCoordinate::toPoint() const
|
||||||
|
{
|
||||||
|
return {x,y};
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF UtmCoordinate::geographic(double offsetX, double offsetY) const
|
||||||
|
{
|
||||||
|
QPointF ret;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GeographicLib::UTMUPS::Reverse(zone, northp, x + offsetX, y + offsetY, ret.ry(), ret.rx());
|
||||||
|
incrementCounter(s_conversion_counter_UTMUPS_Reverse);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error while calculating geographic from utm %d, %9.6f, %9.6f: %s\n", zone, x + offsetX, y + offsetY, e.what());
|
||||||
|
ret = QPointF();
|
||||||
|
}
|
||||||
|
return ret * deg2rad();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF UtmCoordinate::geographic(const QPointF &offset) const
|
||||||
|
{
|
||||||
|
return geographic(offset.x(), offset.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF UtmCoordinate::relativeTo(const UtmCoordinate &other) const
|
||||||
|
{
|
||||||
|
if (zone == other.zone)
|
||||||
|
return QPointF(x - other.x, y - other.y);
|
||||||
|
|
||||||
|
UtmCoordinate otherInMyZone = fromGeographic(other.geographic(), zone);
|
||||||
|
return QPointF(x - otherInMyZone.x, y - otherInMyZone.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
UtmCoordinate UtmCoordinate::fromGeographic(double lon, double lat, int setZone)
|
||||||
|
{
|
||||||
|
|
||||||
|
lon *= rad2deg();
|
||||||
|
lat *= rad2deg();
|
||||||
|
|
||||||
|
UtmCoordinate ret;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
GeographicLib::UTMUPS::Forward(lat, lon, ret.zone, ret.northp, ret.x, ret.y, setZone);
|
||||||
|
incrementCounter(s_conversion_counter_UTMUPS_Forward);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error while calculating mgrs base point from %9.6f, %9.6f: %s\n", lat, lon, e.what());
|
||||||
|
ret = UtmCoordinate();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
UtmCoordinate UtmCoordinate::fromGeographic(const QPointF &lonLat, int setZone)
|
||||||
|
{
|
||||||
|
return fromGeographic(lonLat.x(), lonLat.y(), setZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
UtmCoordinate UtmCoordinate::fromMgrs(const QString &mgrs)
|
||||||
|
{
|
||||||
|
UtmCoordinate utm;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int prec;
|
||||||
|
|
||||||
|
GeographicLib::MGRS::Reverse(cleanupMgrsString(mgrs), utm.zone, utm.northp, utm.x, utm.y, prec, true);
|
||||||
|
incrementCounter(s_conversion_counter_MGRS_Reverse);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error while converting mgrs coordinate %s to utm: %s\n", qPrintable(mgrs), e.what());
|
||||||
|
utm = UtmCoordinate();
|
||||||
|
}
|
||||||
|
return utm;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString mgrsFromGeographic(double lon, double lat, int prec, bool withSpaces)
|
||||||
|
{
|
||||||
|
return UtmCoordinate::fromGeographic(lon, lat, -1).mgrs(prec, withSpaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF geographicFromMgrs(const QString &mgrs)
|
||||||
|
{
|
||||||
|
return UtmCoordinate::fromMgrs(mgrs).geographic();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<UtmCoordinate> &staticDefaultSquaresHelper()
|
||||||
|
{
|
||||||
|
static QVector<UtmCoordinate> s_defaultMgrsSquares;
|
||||||
|
return s_defaultMgrsSquares;
|
||||||
|
}
|
||||||
|
UtmCoordinate &staticPreferedMgrsSquareHelper()
|
||||||
|
{
|
||||||
|
static UtmCoordinate s_preferedMgrsSquare;
|
||||||
|
return s_preferedMgrsSquare;
|
||||||
|
}
|
||||||
|
QVector<UtmCoordinate> &staticMgrsSquaresWithOffsetsHelper()
|
||||||
|
{
|
||||||
|
static QVector<UtmCoordinate> s_mgrsSquaresWithOffsets;
|
||||||
|
return s_mgrsSquaresWithOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QRectF addMarginToArea(const QRectF &rect_LatLon_radiant, const QSize &marginMeter)
|
||||||
|
{
|
||||||
|
double lonMin, lonMax, latMin, latMmax;
|
||||||
|
rect_LatLon_radiant.normalized().getCoords(&lonMin, &latMin, &lonMax, &latMmax);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LtsCoordTrans::UtmCoordinate basePoint;
|
||||||
|
{
|
||||||
|
|
||||||
|
const QPointF centerRad = rect_LatLon_radiant.center();
|
||||||
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1);
|
||||||
|
mgrs += QStringLiteral("00000000000000");
|
||||||
|
|
||||||
|
|
||||||
|
basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin, basePoint.zone));
|
||||||
|
const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax, basePoint.zone));
|
||||||
|
|
||||||
|
int offsetX = marginMeter.width();
|
||||||
|
int offsetY = marginMeter.height();
|
||||||
|
|
||||||
|
const QPointF topLeft(lowerLeft.geographic(-offsetX, -offsetY));
|
||||||
|
const QPointF bottomRight(upperRight.geographic(offsetX, offsetY));
|
||||||
|
|
||||||
|
QRectF res = QRectF(topLeft, bottomRight);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVector<UtmCoordinate> getMgrsSquares(const QRectF &rect, bool setAsDefaultSquares, bool setAsPreferedSquare, bool setAsSquareWithMargin)
|
||||||
|
{
|
||||||
|
QVector<UtmCoordinate> ret = getMgrsSquaresInternal(rect);
|
||||||
|
|
||||||
|
if (setAsDefaultSquares)
|
||||||
|
staticDefaultSquaresHelper() = ret;
|
||||||
|
|
||||||
|
if (setAsPreferedSquare)
|
||||||
|
staticPreferedMgrsSquareHelper() = LtsCoordTrans::UtmCoordinate::fromGeographic(rect.center());
|
||||||
|
|
||||||
|
if (setAsSquareWithMargin)
|
||||||
|
staticMgrsSquaresWithOffsetsHelper() = ret;
|
||||||
|
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<UtmCoordinate> getMgrsSquaresInternal(const QRectF &rect)
|
||||||
|
{
|
||||||
|
QVector<UtmCoordinate> ret;
|
||||||
|
QVector<QString> tmpMgrsSquares;
|
||||||
|
|
||||||
|
|
||||||
|
double lonMin, lonMax, latMin, latMmax;
|
||||||
|
rect.normalized().getCoords(&lonMin, &latMin, &lonMax, &latMmax);
|
||||||
|
|
||||||
|
|
||||||
|
bool withInSingleUtmSquare = true;
|
||||||
|
{
|
||||||
|
const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax));
|
||||||
|
const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin));
|
||||||
|
withInSingleUtmSquare = upperRight.zone == lowerLeft.zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LtsCoordTrans::UtmCoordinate basePoint;
|
||||||
|
{
|
||||||
|
|
||||||
|
QPointF centerRad = rect.center();
|
||||||
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(centerRad.x(), centerRad.y(), -1);
|
||||||
|
mgrs += QStringLiteral("00000000000000");
|
||||||
|
|
||||||
|
|
||||||
|
basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UtmCoordinate upperRight(UtmCoordinate::fromGeographic(lonMax, latMmax, basePoint.zone));
|
||||||
|
const UtmCoordinate lowerLeft(UtmCoordinate::fromGeographic(lonMin, latMin, basePoint.zone));
|
||||||
|
|
||||||
|
const double maxX(upperRight.x - lowerLeft.x);
|
||||||
|
const double maxY(upperRight.y - lowerLeft.y);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const int increment = withInSingleUtmSquare ? 10000 : 1000;
|
||||||
|
for (double x = 0; x <= maxX + increment; x += increment)
|
||||||
|
{
|
||||||
|
const double xOfs = std::min(x, maxX);
|
||||||
|
for (double y = 0; y <= maxY + increment; y += increment)
|
||||||
|
{
|
||||||
|
const double yOfs = std::min(y, maxY);
|
||||||
|
|
||||||
|
const UtmCoordinate utm(UtmCoordinate::fromGeographic(lowerLeft.geographic(xOfs, yOfs)));
|
||||||
|
const QString mgrs(utm.mgrs(0));
|
||||||
|
|
||||||
|
if (!tmpMgrsSquares.contains(mgrs))
|
||||||
|
{
|
||||||
|
tmpMgrsSquares += mgrs;
|
||||||
|
ret += utm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList getMgrsSurroundingSquares(const QRectF &geoRect, const QSize &offsetMeter)
|
||||||
|
{
|
||||||
|
|
||||||
|
const QVector<UtmCoordinate> mainUtms = getMgrsSquaresInternal(geoRect);
|
||||||
|
QStringList mainSquares;
|
||||||
|
mainSquares.reserve(mainUtms.count());
|
||||||
|
for (const auto &utm : mainUtms)
|
||||||
|
mainSquares.append(utm.mgrs(0));
|
||||||
|
|
||||||
|
|
||||||
|
const QRectF rectWithOffset = addMarginToArea(geoRect,offsetMeter);
|
||||||
|
|
||||||
|
QVector<UtmCoordinate> mainUtmsWithOffset = getMgrsSquaresInternal(rectWithOffset);
|
||||||
|
QStringList mainSquaresWithOffset;
|
||||||
|
mainSquaresWithOffset.reserve(mainUtmsWithOffset.count());
|
||||||
|
for (const auto &utm : qAsConst(mainUtmsWithOffset))
|
||||||
|
mainSquaresWithOffset.append(utm.mgrs(0));
|
||||||
|
|
||||||
|
|
||||||
|
QStringList surroundingSquares;
|
||||||
|
surroundingSquares.reserve(mainSquaresWithOffset.count());
|
||||||
|
for (const auto & mgrs0 : mainSquaresWithOffset)
|
||||||
|
{
|
||||||
|
if (!mainSquares.contains(mgrs0))
|
||||||
|
surroundingSquares.append(mgrs0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return surroundingSquares;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<UtmCoordinate> defaultMgrsSquareWithOffsets()
|
||||||
|
{
|
||||||
|
return staticMgrsSquaresWithOffsetsHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setDefaultMgrsSquares(const QVector<QString> &squares)
|
||||||
|
{
|
||||||
|
QVector<UtmCoordinate> coords;
|
||||||
|
coords.reserve(squares.size());
|
||||||
|
for (const QString &s : squares)
|
||||||
|
coords += UtmCoordinate::fromMgrs(s + QStringLiteral("00000000000000"));
|
||||||
|
staticDefaultSquaresHelper() = coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<UtmCoordinate> defaultMgrsSquares()
|
||||||
|
{
|
||||||
|
return staticDefaultSquaresHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
UtmCoordinate preferedMgrsSquare()
|
||||||
|
{
|
||||||
|
return staticPreferedMgrsSquareHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool lonLatAltToGeocentric(double lon, double lat, double altitude, double &xOut, double &yOut, double &zOut)
|
||||||
|
{
|
||||||
|
|
||||||
|
lon *= rad2deg();
|
||||||
|
lat *= rad2deg();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GeographicLib::Geocentric::WGS84().Forward(lat, lon, altitude, xOut, yOut, zOut);
|
||||||
|
incrementCounter(s_conversion_counter_WGS84_Forward);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
xOut = 0;
|
||||||
|
yOut = 0;
|
||||||
|
zOut = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool geocentric2LonLatAlt(double x, double y, double z,
|
||||||
|
double &lon, double &lat, double &altitude)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GeographicLib::Geocentric::WGS84().Reverse(x, y, z, lat, lon, altitude);
|
||||||
|
incrementCounter(s_conversion_counter_WGS84_Reverse);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
lon = 0;
|
||||||
|
lat = 0;
|
||||||
|
altitude = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lon *= deg2rad();
|
||||||
|
lat *= deg2rad();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double distance(const QPointF &p1, const QPointF &p2)
|
||||||
|
{
|
||||||
|
return distance(p1.x(), p1.y(), p2.x(), p2.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
double squareDistance(const QPointF &p1, const QPointF &p2)
|
||||||
|
{
|
||||||
|
return squareDistance(p1.x(), p1.y(), p2.x(), p2.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<< (std::ostream &stream,
|
||||||
|
const UtmCoordinate &coord)
|
||||||
|
{
|
||||||
|
|
||||||
|
stream << "UtmCoordinate(" << std::setprecision(8) << coord.x
|
||||||
|
<< "#" << std::setprecision(8) << coord.y
|
||||||
|
<< " zone=" << coord.zone << ")";
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dumpConversionCounter()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_MGRS_Forward : - mgrs : %ld\n" , s_conversion_counter_MGRS_Forward);
|
||||||
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_MGRS_Reverse - fromMgrs : %ld\n" , s_conversion_counter_MGRS_Reverse);
|
||||||
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_WGS84_Forward - lonLatAltToGeocentric : %ld\n" , s_conversion_counter_WGS84_Forward);
|
||||||
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_WGS84_Reverse - geocentric2LonLatAlt : %ld\n" , s_conversion_counter_WGS84_Reverse);
|
||||||
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_UTMUPS_Forward - fromGeographic : %ld\n" , s_conversion_counter_UTMUPS_Forward);
|
||||||
|
fprintf(stderr, "LtsCoordTrans: s_conversion_counter_UTMUPS_Reverse - geographic : %ld\n" , s_conversion_counter_UTMUPS_Reverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
long getConversionCounter_MGRS_Forward() { return s_conversion_counter_MGRS_Forward; }
|
||||||
|
long getConversionCounter_MGRS_Reverse() { return s_conversion_counter_MGRS_Reverse; }
|
||||||
|
long getConversionCounter_WGS84_Forward() { return s_conversion_counter_WGS84_Forward; }
|
||||||
|
long getConversionCounter_WGS84_Reverse() { return s_conversion_counter_WGS84_Reverse; }
|
||||||
|
long getConversionCounter_UTMUPS_Forward() { return s_conversion_counter_UTMUPS_Forward; }
|
||||||
|
long getConversionCounter_UTMUPS_Reverse() { return s_conversion_counter_UTMUPS_Reverse; }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LtsCoordTrans/ltscoordtrans_export.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cmath>
|
||||||
|
#include <QVector>
|
||||||
|
class QPointF;
|
||||||
|
class QRectF;
|
||||||
|
class QSize;
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
namespace LtsCoordTrans
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline Q_CONSTEXPR double pi() { return M_PI; }
|
||||||
|
inline Q_CONSTEXPR double deg2rad() { return pi() / 180.; }
|
||||||
|
inline Q_CONSTEXPR double rad2deg() { return 1. / deg2rad(); }
|
||||||
|
|
||||||
|
struct LTSCOORDTRANS_EXPORT UtmCoordinate
|
||||||
|
{
|
||||||
|
int zone = -1;
|
||||||
|
bool northp = true;
|
||||||
|
double x = 0.;
|
||||||
|
double y = 0.;
|
||||||
|
|
||||||
|
UtmCoordinate() = default;
|
||||||
|
UtmCoordinate(int zone_, double x_, double y_, bool northp_ = true)
|
||||||
|
: zone(zone_)
|
||||||
|
, northp(northp_)
|
||||||
|
, x(x_)
|
||||||
|
, y(y_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool isValid() const { return zone > 0 && zone <= 60; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QString mgrs(int prec = 5, bool withSpaces = true) const;
|
||||||
|
|
||||||
|
QPointF toPoint() const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QPointF geographic(double offsetX = 0., double offsetY = 0.) const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QPointF geographic(const QPointF &offset) const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QPointF relativeTo(const UtmCoordinate &other) const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static UtmCoordinate fromGeographic(double lon, double lat, int setZone = -1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static UtmCoordinate fromGeographic(const QPointF &lonLat, int setZone = -1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static UtmCoordinate fromMgrs(const QString &mgrs);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT QString mgrsFromGeographic(double lon, double lat, int prec = 5, bool withSpaces = true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT QPointF geographicFromMgrs(const QString &mgrs);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT QRectF addMarginToArea(const QRectF &rect_LatLon_radiant, const QSize &marginMeter);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT QVector<UtmCoordinate> getMgrsSquares(const QRectF &rect, bool setAsDefaultSquares, bool setAsPreferedSquare, bool setAsSquareWithMargin = false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QVector<UtmCoordinate> getMgrsSquaresInternal(const QRectF &rect);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QStringList getMgrsSurroundingSquares(const QRectF &geoRect, const QSize &offsetMeter);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT QVector<UtmCoordinate> defaultMgrsSquareWithOffsets();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT void setDefaultMgrsSquares(const QVector<QString> &squares);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT QVector<UtmCoordinate> defaultMgrsSquares();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT UtmCoordinate preferedMgrsSquare();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT bool lonLatAltToGeocentric(double lon, double lat, double altitude, double &xOut, double &yOut, double &zOut);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT bool geocentric2LonLatAlt(double x, double y, double z,
|
||||||
|
double &lon, double &lat, double &altitude);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline double squareDistance(const UtmCoordinate &p1, const UtmCoordinate &p2)
|
||||||
|
{
|
||||||
|
if (p1.zone != p2.zone)
|
||||||
|
{
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return std::nan("");
|
||||||
|
}
|
||||||
|
return std::pow(p1.x - p2.x, 2) + std::pow(p1.y - p2.y, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT double squareDistance(const QPointF &p1, const QPointF &p2);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline double squareDistance(const double lon1, const double lat1, const double lon2, const double lat2)
|
||||||
|
{
|
||||||
|
const auto p1(UtmCoordinate::fromGeographic(lon1, lat1));
|
||||||
|
const auto p2(UtmCoordinate::fromGeographic(lon2, lat2, p1.zone));
|
||||||
|
return squareDistance(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline double squareDistance(const UtmCoordinate &p1, const double lon2, const double lat2)
|
||||||
|
{
|
||||||
|
const auto p2(UtmCoordinate::fromGeographic(lon2, lat2, p1.zone));
|
||||||
|
return squareDistance(p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline double distance(const UtmCoordinate &p1, const UtmCoordinate &p2)
|
||||||
|
{
|
||||||
|
if (p1.zone != p2.zone)
|
||||||
|
{
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return std::nan("");
|
||||||
|
}
|
||||||
|
return sqrt(squareDistance(p1, p2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT double distance(const QPointF &p1, const QPointF &p2);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline double distance(const double lon1, const double lat1, const double lon2, const double lat2)
|
||||||
|
{
|
||||||
|
return sqrt(squareDistance(lon1, lat1, lon2, lat2));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double distance(const UtmCoordinate &p1, const double lon2, const double lat2)
|
||||||
|
{
|
||||||
|
return sqrt(squareDistance(p1, lon2, lat2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT void dumpConversionCounter();
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT long getConversionCounter_MGRS_Forward();
|
||||||
|
LTSCOORDTRANS_EXPORT long getConversionCounter_MGRS_Reverse();
|
||||||
|
LTSCOORDTRANS_EXPORT long getConversionCounter_WGS84_Forward();
|
||||||
|
LTSCOORDTRANS_EXPORT long getConversionCounter_WGS84_Reverse();
|
||||||
|
LTSCOORDTRANS_EXPORT long getConversionCounter_UTMUPS_Forward();
|
||||||
|
LTSCOORDTRANS_EXPORT long getConversionCounter_UTMUPS_Reverse();
|
||||||
|
|
||||||
|
|
||||||
|
LTSCOORDTRANS_EXPORT std::ostream &operator<< (std::ostream &stream,
|
||||||
|
const UtmCoordinate &coord);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LTSCOORDTRANS_EXPORT UtmCoordinate3D : public LtsCoordTrans::UtmCoordinate
|
||||||
|
{
|
||||||
|
using BaseClass = LtsCoordTrans::UtmCoordinate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UtmCoordinate3D()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit UtmCoordinate3D(const LtsCoordTrans::UtmCoordinate &utm2d, double _zValue = std::nan(""))
|
||||||
|
:BaseClass()
|
||||||
|
{
|
||||||
|
zone = utm2d.zone;
|
||||||
|
northp = utm2d.northp;
|
||||||
|
x = utm2d.x;
|
||||||
|
y = utm2d.y;
|
||||||
|
zValue = _zValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
zone = -1;
|
||||||
|
northp = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
x = 0.;
|
||||||
|
y = 0.;
|
||||||
|
zValue = std::nan("");
|
||||||
|
}
|
||||||
|
|
||||||
|
double z() const { return zValue; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double zValue{std::nan("")};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+469
@@ -0,0 +1,469 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "lts_coord_trans_unit_test.h"
|
||||||
|
#include "lts_coord_trans.h"
|
||||||
|
#include <GeographicLib/Math.hpp>
|
||||||
|
#include <QTest>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
QTEST_MAIN(coordtrans_test)
|
||||||
|
|
||||||
|
void coordtrans_test::initTestCase()
|
||||||
|
{
|
||||||
|
qDebug("sizeof(UtmCoordinate) = %lu", sizeof(LtsCoordTrans::UtmCoordinate));
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::cleanupTestCase()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void coordtrans_test::testFromGeographic()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LtsCoordTrans::UtmCoordinate utm;
|
||||||
|
QPointF lonlat;
|
||||||
|
|
||||||
|
lonlat = QPointF(8.6264954126, 53.1138437183) * LtsCoordTrans::deg2rad();
|
||||||
|
utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y(), -1);
|
||||||
|
QCOMPARE(utm.mgrs(0), QStringLiteral("32U MD"));
|
||||||
|
utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y());
|
||||||
|
QCOMPARE(utm.mgrs(8), QStringLiteral("32U MD 74999999 84999999"));
|
||||||
|
|
||||||
|
lonlat = QPointF(11.3003696521, 52.8224535191) * LtsCoordTrans::deg2rad();
|
||||||
|
utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y(), -1);
|
||||||
|
QCOMPARE(utm.mgrs(0), QStringLiteral("32U PD"));
|
||||||
|
utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y());
|
||||||
|
QCOMPARE(utm.mgrs(5), QStringLiteral("32U PD 55000 55000"));
|
||||||
|
|
||||||
|
lonlat = QPointF(54.8821607021, 24.2631973752) * LtsCoordTrans::deg2rad();
|
||||||
|
utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y(), -1);
|
||||||
|
QCOMPARE(utm.mgrs(0), QStringLiteral("40R BM"));
|
||||||
|
utm = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y());
|
||||||
|
QCOMPARE(utm.mgrs(5), QStringLiteral("40R BM 85000 85000"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
utm = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("40R BM 00000 00000"));
|
||||||
|
QCOMPARE(utm.mgrs(0), QStringLiteral("40Q BM"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::testMgrsFromGeographic()
|
||||||
|
{
|
||||||
|
|
||||||
|
QString mgrs;
|
||||||
|
QPointF lonlat;
|
||||||
|
|
||||||
|
lonlat = QPointF(8.6191049344, 53.1048309844) * LtsCoordTrans::deg2rad();
|
||||||
|
mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 9);
|
||||||
|
QCOMPARE(mgrs, QStringLiteral("32U MD 745000000 840000000"));
|
||||||
|
|
||||||
|
mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 8, false);
|
||||||
|
QCOMPARE(mgrs, QStringLiteral("32UMD7450000084000000"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::testRelativeMetricalOffet_data()
|
||||||
|
{
|
||||||
|
|
||||||
|
QTest::addColumn<QPoint>("offset");
|
||||||
|
QTest::addColumn<QString>("correctMgrs");
|
||||||
|
|
||||||
|
|
||||||
|
QTest::newRow("set 1") << QPoint( 5, 5) << QStringLiteral("32U PD 99995 99995");
|
||||||
|
|
||||||
|
|
||||||
|
QTest::newRow("set 2") << QPoint(100, 100) << QStringLiteral("32U QE 00090 00090");
|
||||||
|
QTest::newRow("set 3") << QPoint(500, 500) << QStringLiteral("32U QE 00490 00490");
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::testRelativeMetricalOffet()
|
||||||
|
{
|
||||||
|
QFETCH(QPoint, offset);
|
||||||
|
QFETCH(QString, correctMgrs);
|
||||||
|
|
||||||
|
const LtsCoordTrans::UtmCoordinate &utmRef = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U PD 99990 99990"));
|
||||||
|
qDebug() << "utmRef = " << utmRef.mgrs();
|
||||||
|
|
||||||
|
{
|
||||||
|
LtsCoordTrans::UtmCoordinate utmRaw;
|
||||||
|
utmRaw.x = utmRef.x + offset.x();
|
||||||
|
utmRaw.y = utmRef.y + offset.y();
|
||||||
|
utmRaw.zone = utmRef.zone;
|
||||||
|
qDebug() << "offset = " << offset << " - utmRaw = " << utmRaw.mgrs();
|
||||||
|
|
||||||
|
QCOMPARE(utmRaw.mgrs(), correctMgrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
const LtsCoordTrans::UtmCoordinate utmSimple(utmRef.zone,
|
||||||
|
utmRef.x + offset.x(),
|
||||||
|
utmRef.y + offset.y());
|
||||||
|
|
||||||
|
qDebug() << "offset = " << offset << " - utmSimple = " << utmSimple.mgrs();
|
||||||
|
QCOMPARE(utmSimple.mgrs(), correctMgrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF createArea(const QString &base,
|
||||||
|
int xCenter, int yCenter, int width, int height)
|
||||||
|
{
|
||||||
|
|
||||||
|
QRectF areaRect = QRectF(xCenter - width / 2, yCenter - height / 2, width, height);
|
||||||
|
LtsCoordTrans::UtmCoordinate utmBase(LtsCoordTrans::UtmCoordinate::fromGeographic(LtsCoordTrans::geographicFromMgrs(base)));
|
||||||
|
const QPointF topLeft(utmBase.geographic(areaRect.left(), areaRect.top()));
|
||||||
|
const QPointF bottomRight(utmBase.geographic(areaRect.right(), areaRect.bottom()));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QRectF rect (topLeft, bottomRight);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void coordtrans_test::testAddMarginToArea()
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
qDebug() << "Altmark";
|
||||||
|
QRectF area = createArea(QStringLiteral("32U PD 0000000000"), 50000, 10000, 30000, 30000);
|
||||||
|
QRectF area1Increased = LtsCoordTrans::addMarginToArea(area, QSize(5000, 20000));
|
||||||
|
|
||||||
|
qDebug() << "topLeft : " << qSetRealNumberPrecision(15) << area.topLeft() << " - degree:" << area.topLeft()*LtsCoordTrans::rad2deg();
|
||||||
|
qDebug() << "topLeftInc : " << qSetRealNumberPrecision(15) << area1Increased.topLeft() << " - degree:" << area1Increased.topLeft()*LtsCoordTrans::rad2deg();
|
||||||
|
|
||||||
|
qDebug() << "bottomRight : " << qSetRealNumberPrecision(15) << area.bottomRight() << " - degree:" << area.bottomRight()*LtsCoordTrans::rad2deg();
|
||||||
|
qDebug() << "bottomRightInc : " << qSetRealNumberPrecision(15) << area1Increased.bottomRight() << " - degree:" << area1Increased.bottomRight()*LtsCoordTrans::rad2deg();
|
||||||
|
|
||||||
|
QVERIFY2( area1Increased.topLeft().x() < area.topLeft().x(), "addMarginToArea topLeft X failed");
|
||||||
|
QVERIFY2( area1Increased.topLeft().y() < area.topLeft().y(), "addMarginToArea topLeft Y failed");
|
||||||
|
|
||||||
|
QVERIFY2( area1Increased.bottomRight().x() > area.bottomRight().x(), "addMarginToArea bottomRight X failed");
|
||||||
|
QVERIFY2( area1Increased.bottomRight().y() > area.bottomRight().y(), "addMarginToArea bottomRight Y failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
qDebug() << "Luxemburg";
|
||||||
|
QRectF area = createArea(QStringLiteral("32U LA 0000000000"), 100, 100, 5000, 5000);
|
||||||
|
QRectF area1Increased = LtsCoordTrans::addMarginToArea(area, QSize(50000, 20000));
|
||||||
|
|
||||||
|
qDebug() << "topLeft : " << qSetRealNumberPrecision(15) << area.topLeft() << " - degree:" << area.topLeft()*LtsCoordTrans::rad2deg();
|
||||||
|
qDebug() << "topLeftInc : " << qSetRealNumberPrecision(15) << area1Increased.topLeft() << " - degree:" << area1Increased.topLeft()*LtsCoordTrans::rad2deg();
|
||||||
|
|
||||||
|
qDebug() << "bottomRight : " << qSetRealNumberPrecision(15) << area.bottomRight() << " - degree:" << area.bottomRight()*LtsCoordTrans::rad2deg();
|
||||||
|
qDebug() << "bottomRightInc : " << qSetRealNumberPrecision(15) << area1Increased.bottomRight() << " - degree:" << area1Increased.bottomRight()*LtsCoordTrans::rad2deg();
|
||||||
|
|
||||||
|
QVERIFY2( area1Increased.topLeft().x() < area.topLeft().x(), "addMarginToArea topLeft X failed");
|
||||||
|
QVERIFY2( area1Increased.topLeft().y() < area.topLeft().y(), "addMarginToArea topLeft Y failed");
|
||||||
|
|
||||||
|
QVERIFY2( area1Increased.bottomRight().x() > area.bottomRight().x(), "addMarginToArea bottomRight X failed");
|
||||||
|
QVERIFY2( area1Increased.bottomRight().y() > area.bottomRight().y(), "addMarginToArea bottomRight Y failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::testIncreaseRect()
|
||||||
|
{
|
||||||
|
|
||||||
|
QPointF lonlat = QPointF(8.6191049344, 53.1048309844) * LtsCoordTrans::deg2rad();
|
||||||
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 9);
|
||||||
|
QCOMPARE(mgrs, QStringLiteral("32U MD 745000000 840000000"));
|
||||||
|
|
||||||
|
const LtsCoordTrans::UtmCoordinate centerPoint = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y());
|
||||||
|
|
||||||
|
LtsCoordTrans::UtmCoordinate topLeftUtm;
|
||||||
|
LtsCoordTrans::UtmCoordinate topLeftUtm2;
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
QPointF topLeftGeo = centerPoint.geographic(-1111, 2222);
|
||||||
|
QPointF bottomRightGeo = centerPoint.geographic( 3000, -4000);
|
||||||
|
|
||||||
|
topLeftUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(topLeftGeo);
|
||||||
|
const LtsCoordTrans::UtmCoordinate bottomRightUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(bottomRightGeo);
|
||||||
|
qDebug() << "topLeftUtm: " << topLeftUtm.x << " - " << qSetRealNumberPrecision(15) << topLeftUtm.y;
|
||||||
|
qDebug() << "bottomRightUtm: " << bottomRightUtm.x << " - " << qSetRealNumberPrecision(15) << bottomRightUtm.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
topLeftUtm2.x = centerPoint.x - 1111;
|
||||||
|
topLeftUtm2.y = centerPoint.y + 2222;
|
||||||
|
|
||||||
|
LtsCoordTrans::UtmCoordinate bottomRightUtm2;
|
||||||
|
bottomRightUtm2.x = centerPoint.x + 3000;
|
||||||
|
bottomRightUtm2.y = centerPoint.y - 4000;
|
||||||
|
|
||||||
|
qDebug() << "topLeftUtm2: " << topLeftUtm2.x << " - " << qSetRealNumberPrecision(15) << topLeftUtm2.y;
|
||||||
|
qDebug() << "bottomRightUtm2: " << bottomRightUtm2.x << " - " << qSetRealNumberPrecision(15) << bottomRightUtm2.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY2(qFuzzyCompare(topLeftUtm.x, topLeftUtm2.x), "testIncreaseRect x failed");
|
||||||
|
QVERIFY2(qFuzzyCompare(topLeftUtm.y, topLeftUtm2.y), "testIncreaseRect y failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::testIncreaseRect2()
|
||||||
|
{
|
||||||
|
|
||||||
|
QPointF lonlat = QPointF(8.6191049344, 53.1048309844) * LtsCoordTrans::deg2rad();
|
||||||
|
QString mgrs = LtsCoordTrans::mgrsFromGeographic(lonlat.x(), lonlat.y(), 9);
|
||||||
|
QCOMPARE(mgrs, QStringLiteral("32U MD 745000000 840000000"));
|
||||||
|
|
||||||
|
QRectF myRect;
|
||||||
|
QRectF myRectIncreased;
|
||||||
|
|
||||||
|
{
|
||||||
|
const LtsCoordTrans::UtmCoordinate centerPoint = LtsCoordTrans::UtmCoordinate::fromGeographic(lonlat.x(), lonlat.y());
|
||||||
|
|
||||||
|
QPointF topLeftGeo = centerPoint.geographic(-3000, 2000);
|
||||||
|
QPointF bottomRightGeo = centerPoint.geographic( 3000, -2000);
|
||||||
|
myRect = QRectF(topLeftGeo, bottomRightGeo).normalized();
|
||||||
|
|
||||||
|
qDebug() << "myRect : " << myRect;
|
||||||
|
qDebug() << "myRect center : " << myRect.center();
|
||||||
|
qDebug() << "myRect height : " << myRect.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
const QPointF centerGeo = myRect.center();
|
||||||
|
const LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(centerGeo);
|
||||||
|
|
||||||
|
QPointF topLeftGeo = centerUtm.geographic(-6000, 4000);
|
||||||
|
QPointF bottomRightGeo = centerUtm.geographic( 6000, -4000);
|
||||||
|
|
||||||
|
myRectIncreased = QRectF(topLeftGeo, bottomRightGeo).normalized();
|
||||||
|
|
||||||
|
qDebug() << "myRectIncreased : " << myRectIncreased;
|
||||||
|
qDebug() << "myRectIncreased center: " << myRectIncreased.center();
|
||||||
|
qDebug() << "myRectIncreased height: " << myRectIncreased.height();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVERIFY2( (int)(myRectIncreased.center().x()*10000) == (int)(myRect.center().x()*10000), "testIncreaseRect2 centerX failed");
|
||||||
|
QVERIFY2( (int)(myRectIncreased.center().y()*10000) == (int)(myRect.center().y()*10000), "testIncreaseRect2 centerY failed");
|
||||||
|
|
||||||
|
QVERIFY2(myRectIncreased.width() > myRect.width(), "testIncreaseRect2 width failed");
|
||||||
|
QVERIFY2(myRectIncreased.height() > myRect.height(), "testIncreaseRect2 height failed");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void coordtrans_test::testGeographicFromMgrs()
|
||||||
|
{
|
||||||
|
|
||||||
|
QString mgrs(QStringLiteral("32UPD7654398765"));
|
||||||
|
|
||||||
|
|
||||||
|
QPointF geographic = LtsCoordTrans::geographicFromMgrs(mgrs);
|
||||||
|
QCOMPARE(geographic, QPointF(11.6436815550, 53.2088411454) * LtsCoordTrans::deg2rad());
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::testMgrsSquares()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkArea(QStringLiteral("32U PD 0000000000"), 50000, 10000, 30000, 30000, { QStringLiteral("32U PC"),
|
||||||
|
QStringLiteral("32U PD")
|
||||||
|
});
|
||||||
|
checkArea(QStringLiteral("32U MD 0000000000"), 50000, 10000, 30000, 30000, { QStringLiteral("32U MC"),
|
||||||
|
QStringLiteral("32U MD")
|
||||||
|
});
|
||||||
|
checkArea(QStringLiteral("32U MD 0000000000"), 89999, 89999, 21000, 21000, { QStringLiteral("32U MD"),
|
||||||
|
QStringLiteral("32U ME"),
|
||||||
|
QStringLiteral("32U ND"),
|
||||||
|
QStringLiteral("32U NE")
|
||||||
|
});
|
||||||
|
|
||||||
|
checkArea(QStringLiteral("39Q XG 0000000000"), 40000, 35000, 60000, 70000, { QStringLiteral("39Q XG"),
|
||||||
|
QStringLiteral("39R XG")
|
||||||
|
});
|
||||||
|
checkArea(QStringLiteral("40R CP 0000000000"), 94000, 9000, 7000, 16000, { QStringLiteral("40R CP") });
|
||||||
|
checkArea(QStringLiteral("40R DN 0000000000"), 500, 95500, 12000, 7000, { QStringLiteral("40R CN"),
|
||||||
|
QStringLiteral("40R DN")
|
||||||
|
});
|
||||||
|
checkArea(QStringLiteral("40R DN 0000000000"), 500, 96760, 30000, 30000, { QStringLiteral("40R CN"),
|
||||||
|
QStringLiteral("40R CP"),
|
||||||
|
QStringLiteral("40R DN"),
|
||||||
|
QStringLiteral("40R DP")
|
||||||
|
});
|
||||||
|
checkArea(QStringLiteral("40R BM 0000000000"), 59000, 59500, 46000, 40000, { QStringLiteral("40Q BM"),
|
||||||
|
QStringLiteral("40R BM")
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkArea(QStringLiteral("32U LA 0000000000"), 10000, 10000, 5000, 5000, { QStringLiteral("32U LA"),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
checkArea(QStringLiteral("32U LA 0000000000"), 100, 100, 60000, 60000, { QStringLiteral("31U GQ"),
|
||||||
|
QStringLiteral("31U GR"),
|
||||||
|
QStringLiteral("32U KA"),
|
||||||
|
QStringLiteral("32U KV"),
|
||||||
|
QStringLiteral("32U LA"),
|
||||||
|
QStringLiteral("32U LV")
|
||||||
|
});
|
||||||
|
|
||||||
|
checkArea(QStringLiteral("32U LA 0000000000"), 100, 100,150000,150000, { QStringLiteral("31U FQ"),
|
||||||
|
QStringLiteral("31U FR"),
|
||||||
|
QStringLiteral("31U GQ"),
|
||||||
|
QStringLiteral("31U GR"),
|
||||||
|
QStringLiteral("32U KA"),
|
||||||
|
QStringLiteral("32U KV"),
|
||||||
|
QStringLiteral("32U LA"),
|
||||||
|
QStringLiteral("32U LV")
|
||||||
|
});
|
||||||
|
|
||||||
|
checkArea(QStringLiteral("32U LA 0000000000"), 100, 100,200000,200000, { QStringLiteral("31U FP"),
|
||||||
|
QStringLiteral("31U FQ"),
|
||||||
|
QStringLiteral("31U FR"),
|
||||||
|
QStringLiteral("31U GP"),
|
||||||
|
QStringLiteral("31U GQ"),
|
||||||
|
QStringLiteral("31U GR"),
|
||||||
|
QStringLiteral("31U GS"),
|
||||||
|
QStringLiteral("32U KA"),
|
||||||
|
QStringLiteral("32U KB"),
|
||||||
|
QStringLiteral("32U KV"),
|
||||||
|
QStringLiteral("32U LA"),
|
||||||
|
QStringLiteral("32U LB"),
|
||||||
|
QStringLiteral("32U LV"),
|
||||||
|
QStringLiteral("32U MA"),
|
||||||
|
QStringLiteral("32U MB"),
|
||||||
|
QStringLiteral("32U MV")
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVector<LtsCoordTrans::UtmCoordinate> checkAreaGetMgrsSquares(const QString &base,
|
||||||
|
int xCenter, int yCenter, int width, int height)
|
||||||
|
{
|
||||||
|
|
||||||
|
QRectF areaRect = QRectF(xCenter - width / 2, yCenter - height / 2, width, height);
|
||||||
|
LtsCoordTrans::UtmCoordinate utmBase(LtsCoordTrans::UtmCoordinate::fromGeographic(LtsCoordTrans::geographicFromMgrs(base)));
|
||||||
|
const QPointF topLeft(utmBase.geographic(areaRect.left(), areaRect.top()));
|
||||||
|
const QPointF bottomRight(utmBase.geographic(areaRect.right(), areaRect.bottom()));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const auto squares = LtsCoordTrans::getMgrsSquares(QRectF(topLeft, bottomRight), false, false);
|
||||||
|
|
||||||
|
QStringList src;
|
||||||
|
src.reserve(squares.size());
|
||||||
|
for (const auto &utm : squares)
|
||||||
|
src += utm.mgrs(0);
|
||||||
|
src.sort();
|
||||||
|
|
||||||
|
|
||||||
|
return squares;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::testMgrsSquaresWithSurrounding()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int initialSize = 5000;
|
||||||
|
|
||||||
|
|
||||||
|
QVector<LtsCoordTrans::UtmCoordinate> squares1 = checkAreaGetMgrsSquares(QStringLiteral("32U NC 0000000000"), 50000, 50000, initialSize, initialSize);
|
||||||
|
|
||||||
|
QVERIFY2(squares1.size() == 1, "checkAreaGetMgrsSquares failed");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int biggerSize = initialSize + (1*110000);
|
||||||
|
QVector<LtsCoordTrans::UtmCoordinate> squares9 = checkAreaGetMgrsSquares(QStringLiteral("32U NC 0000000000"), 50000, 50000, biggerSize, biggerSize);
|
||||||
|
|
||||||
|
QVERIFY2(squares9.size() == 9, "checkAreaGetMgrsSquares biggerSize failed");
|
||||||
|
|
||||||
|
int muchBiggerSize = initialSize + (3*110000);
|
||||||
|
QVector<LtsCoordTrans::UtmCoordinate> square33 = checkAreaGetMgrsSquares(QStringLiteral("32U NC 0000000000"), 50000, 50000, muchBiggerSize, muchBiggerSize);
|
||||||
|
|
||||||
|
QVERIFY2(square33.size() == 33, "checkAreaGetMgrsSquares muchBiggerSize failed");
|
||||||
|
|
||||||
|
|
||||||
|
QStringList string1;
|
||||||
|
string1.reserve(squares1.size());
|
||||||
|
for (const LtsCoordTrans::UtmCoordinate &item : qAsConst(squares1))
|
||||||
|
string1.append(item.mgrs(0));
|
||||||
|
|
||||||
|
QStringList string9;
|
||||||
|
string9.reserve(squares9.size());
|
||||||
|
for (const LtsCoordTrans::UtmCoordinate &item : qAsConst(squares9))
|
||||||
|
string9.append(item.mgrs(0));
|
||||||
|
|
||||||
|
QStringList string33;
|
||||||
|
string33.reserve(square33.size());
|
||||||
|
for (const LtsCoordTrans::UtmCoordinate &item : qAsConst(square33))
|
||||||
|
string33.append(item.mgrs(0));
|
||||||
|
|
||||||
|
{
|
||||||
|
QStringList surroundingStrings;
|
||||||
|
surroundingStrings.reserve(squares9.size());
|
||||||
|
for (const QString &str : qAsConst(string9))
|
||||||
|
if (!string1.contains(str))
|
||||||
|
surroundingStrings.append(str);
|
||||||
|
|
||||||
|
QVERIFY2(surroundingStrings.size() == 8, "checkAreaGetMgrsSquares squaresDiff failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QStringList surroundingStrings;
|
||||||
|
surroundingStrings.reserve(string33.size());
|
||||||
|
for (const QString &str : qAsConst(string33))
|
||||||
|
if (!string1.contains(str))
|
||||||
|
surroundingStrings.append(str);
|
||||||
|
|
||||||
|
QVERIFY2(surroundingStrings.size() == 32, "checkAreaGetMgrsSquares squaresDiff failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void coordtrans_test::checkArea(const QString &base,
|
||||||
|
int xCenter, int yCenter, int width, int height,
|
||||||
|
const QStringList &coords)
|
||||||
|
{
|
||||||
|
qDebug() << "checkArea : base: " << base;
|
||||||
|
QRectF areaRect = QRectF(xCenter - width / 2, yCenter - height / 2, width, height);
|
||||||
|
LtsCoordTrans::UtmCoordinate utmBase(LtsCoordTrans::UtmCoordinate::fromGeographic(LtsCoordTrans::geographicFromMgrs(base)));
|
||||||
|
const QPointF topLeft(utmBase.geographic(areaRect.left(), areaRect.top()));
|
||||||
|
const QPointF bottomRight(utmBase.geographic(areaRect.right(), areaRect.bottom()));
|
||||||
|
qDebug() << "TopLeft: " << topLeft * LtsCoordTrans::rad2deg();
|
||||||
|
qDebug() << "BottomRight: " << bottomRight * LtsCoordTrans::rad2deg();
|
||||||
|
|
||||||
|
const auto squares = LtsCoordTrans::getMgrsSquares(QRectF(topLeft, bottomRight), false, false);
|
||||||
|
|
||||||
|
QStringList src;
|
||||||
|
src.reserve(squares.size());
|
||||||
|
for (const auto &utm : squares)
|
||||||
|
src += utm.mgrs(0);
|
||||||
|
src.sort();
|
||||||
|
qDebug() << "src: '" << src << "' expected: '" << coords << "'";
|
||||||
|
QCOMPARE(src, coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class coordtrans_test : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
|
void testFromGeographic();
|
||||||
|
void testMgrsFromGeographic();
|
||||||
|
|
||||||
|
void testRelativeMetricalOffet_data();
|
||||||
|
void testRelativeMetricalOffet();
|
||||||
|
|
||||||
|
void testAddMarginToArea();
|
||||||
|
void testIncreaseRect();
|
||||||
|
void testIncreaseRect2();
|
||||||
|
|
||||||
|
void testGeographicFromMgrs();
|
||||||
|
void testMgrsSquares();
|
||||||
|
|
||||||
|
void testMgrsSquaresWithSurrounding();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void checkArea(const QString &base,
|
||||||
|
int xCenter, int yCenter, int width, int height,
|
||||||
|
const QStringList &coords);
|
||||||
|
|
||||||
|
};
|
||||||
+196
@@ -0,0 +1,196 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "lts_coord_widget.h"
|
||||||
|
#include "lts_coord_widget_p.h"
|
||||||
|
|
||||||
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
|
namespace LtsCoordWidget
|
||||||
|
{
|
||||||
|
|
||||||
|
LtsCoordWidget::LtsCoordWidget(DisplayType dt, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, m_displayType(dt)
|
||||||
|
{
|
||||||
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setCoordinate(const QPointF &geoCoord, bool bUseAsDefaultValue)
|
||||||
|
{
|
||||||
|
if (m_coordinate != geoCoord)
|
||||||
|
emit coordinateChanged(geoCoord);
|
||||||
|
if (bUseAsDefaultValue)
|
||||||
|
m_defaultCoordinate = geoCoord;
|
||||||
|
if (m_inputWidgetHandler)
|
||||||
|
m_inputWidgetHandler->setCoordinate(geoCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::clear()
|
||||||
|
{
|
||||||
|
if (m_inputWidgetHandler)
|
||||||
|
m_inputWidgetHandler->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setCoordinate(double lon, double lat, bool bUseAsDefaultValue)
|
||||||
|
{
|
||||||
|
setCoordinate(QPointF(lon, lat), bUseAsDefaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF LtsCoordWidget::coordinate() const
|
||||||
|
{
|
||||||
|
if (!m_inputWidgetHandler)
|
||||||
|
return QPointF();
|
||||||
|
return m_inputWidgetHandler->coordinate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LtsCoordWidget::DisplayType LtsCoordWidget::displayType() const
|
||||||
|
{
|
||||||
|
return m_displayType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setDisplayType(LtsCoordWidget::DisplayType dt)
|
||||||
|
{
|
||||||
|
if (m_displayType == dt)
|
||||||
|
return;
|
||||||
|
m_displayType = dt;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCoordWidget::withResetButton() const
|
||||||
|
{
|
||||||
|
return m_bWithResetButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setWithResetButton(bool bWith)
|
||||||
|
{
|
||||||
|
if (m_bWithResetButton == bWith)
|
||||||
|
return;
|
||||||
|
m_bWithResetButton = bWith;
|
||||||
|
if (m_inputWidgetHandler)
|
||||||
|
m_inputWidgetHandler->resetButton()->setVisible(m_bWithResetButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCoordWidget::showToolTip() const
|
||||||
|
{
|
||||||
|
return m_bShowToolTip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setShowToolTip(bool bShow)
|
||||||
|
{
|
||||||
|
if (m_bShowToolTip == bShow)
|
||||||
|
return;
|
||||||
|
m_bShowToolTip = bShow;
|
||||||
|
|
||||||
|
updateToolTip();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LtsCoordWidget::mgrsSquareEditable() const
|
||||||
|
{
|
||||||
|
return m_bMgrsSquareEditable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setMgrsSquareEditable(bool bEditable)
|
||||||
|
{
|
||||||
|
if (m_bMgrsSquareEditable == bEditable)
|
||||||
|
return;
|
||||||
|
m_bMgrsSquareEditable = bEditable;
|
||||||
|
if (m_inputWidgetHandler)
|
||||||
|
m_inputWidgetHandler->setSquaresEditable(bEditable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setWithSurroundingMgrsSquares(bool bEnable)
|
||||||
|
{
|
||||||
|
if (m_inputWidgetHandler)
|
||||||
|
m_inputWidgetHandler->setWithSurroundingSquare(bEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::setMgrsSquares(const QVector<LtsCoordTrans::UtmCoordinate> &geoCoords)
|
||||||
|
{
|
||||||
|
if (m_inputWidgetHandler)
|
||||||
|
m_inputWidgetHandler->setMgrsSquares(geoCoords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LtsCoordWidget::event(QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::ToolTip)
|
||||||
|
updateToolTip();
|
||||||
|
return QWidget::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LtsCoordWidget::rebuildUi()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (m_inputWidgetHandler)
|
||||||
|
{
|
||||||
|
m_inputWidgetHandler->cleanup();
|
||||||
|
delete m_inputWidgetHandler;
|
||||||
|
delete layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_displayType)
|
||||||
|
{
|
||||||
|
case DisplayType::MGRS5:
|
||||||
|
m_inputWidgetHandler = new MgrsEdit(5, this);
|
||||||
|
break;
|
||||||
|
case DisplayType::MGRS7:
|
||||||
|
m_inputWidgetHandler = new MgrsEdit(7, this);
|
||||||
|
break;
|
||||||
|
case DisplayType::UTM:
|
||||||
|
|
||||||
|
qt_assert("DisplayType::UTM not yet implemented", __FILE__, __LINE__);
|
||||||
|
break;
|
||||||
|
case DisplayType::WGS84:
|
||||||
|
m_inputWidgetHandler = new Wgs84Edit(this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!m_inputWidgetHandler)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_inputWidgetHandler->setSquaresEditable(m_bMgrsSquareEditable);
|
||||||
|
m_inputWidgetHandler->resetButton()->setVisible(m_bWithResetButton);
|
||||||
|
connect(m_inputWidgetHandler->resetButton(), &QToolButton::clicked,
|
||||||
|
this, &LtsCoordWidget::onResetToDefaultCoordinate);
|
||||||
|
connect(m_inputWidgetHandler, &LtsCoordInputWidgetHandler::coordinateChanged,
|
||||||
|
this, &LtsCoordWidget::coordinateChanged);
|
||||||
|
connect(m_inputWidgetHandler, &LtsCoordInputWidgetHandler::invalidCoordinate,
|
||||||
|
this, &LtsCoordWidget::invalidCoordinate);
|
||||||
|
if (!m_defaultCoordinate.isNull())
|
||||||
|
m_inputWidgetHandler->setCoordinate(m_defaultCoordinate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LtsCoordWidget::updateToolTip()
|
||||||
|
{
|
||||||
|
QString tt;
|
||||||
|
if (m_bShowToolTip)
|
||||||
|
{
|
||||||
|
const QPointF p(coordinate());
|
||||||
|
if (!p.isNull())
|
||||||
|
{
|
||||||
|
const QString mgrs = LtsCoordTrans::mgrsFromGeographic(p.x(), p.y());
|
||||||
|
const QPointF p2(p * LtsCoordTrans::rad2deg());
|
||||||
|
tt = tr("MGRS: %1\nlat/lon: %2/%3").arg(mgrs).arg(p2.y(), 0, 'f', 10).arg(p2.x(), 0, 'f', 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setToolTip(tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LtsCoordWidget::onResetToDefaultCoordinate()
|
||||||
|
{
|
||||||
|
if (!m_inputWidgetHandler)
|
||||||
|
return;
|
||||||
|
if (m_defaultCoordinate.isNull())
|
||||||
|
m_inputWidgetHandler->clear();
|
||||||
|
m_inputWidgetHandler->setCoordinate(m_defaultCoordinate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LtsCoordTrans/ltscoordwidget_export.h>
|
||||||
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QToolButton;
|
||||||
|
|
||||||
|
namespace LtsCoordWidget
|
||||||
|
{
|
||||||
|
class LtsCoordInputWidgetHandler;
|
||||||
|
class LTSCOORDWIDGET_EXPORT LtsCoordWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum class DisplayType
|
||||||
|
{
|
||||||
|
MGRS5,
|
||||||
|
MGRS7,
|
||||||
|
UTM,
|
||||||
|
WGS84,
|
||||||
|
MGRS = DisplayType::MGRS5
|
||||||
|
};
|
||||||
|
Q_ENUM(DisplayType)
|
||||||
|
Q_PROPERTY(DisplayType displayType READ displayType WRITE setDisplayType)
|
||||||
|
Q_PROPERTY(bool withResetButton READ withResetButton WRITE setWithResetButton)
|
||||||
|
Q_PROPERTY(bool showToolTip READ showToolTip WRITE setShowToolTip)
|
||||||
|
Q_PROPERTY(bool mgrsSquareEditable READ mgrsSquareEditable WRITE setMgrsSquareEditable)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
explicit LtsCoordWidget(QWidget *parent = nullptr)
|
||||||
|
: LtsCoordWidget(LtsCoordWidget::DisplayType::MGRS, parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
explicit LtsCoordWidget(DisplayType dt, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
QPointF coordinate() const;
|
||||||
|
DisplayType displayType() const;
|
||||||
|
bool withResetButton() const;
|
||||||
|
bool showToolTip() const;
|
||||||
|
bool mgrsSquareEditable() const;
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
public Q_SLOTS:
|
||||||
|
|
||||||
|
|
||||||
|
void setCoordinate(const QPointF &geoCoord, bool bUseAsDefaultValue = true);
|
||||||
|
void setCoordinate(double lon, double lat, bool bUseAsDefaultValue = true);
|
||||||
|
void setDisplayType(LtsCoordWidget::DisplayType dt);
|
||||||
|
void setWithResetButton(bool bWith);
|
||||||
|
void setShowToolTip(bool bShow);
|
||||||
|
void setMgrsSquareEditable(bool bEditable);
|
||||||
|
void setWithSurroundingMgrsSquares(bool bEnable);
|
||||||
|
void setMgrsSquares(const QVector<LtsCoordTrans::UtmCoordinate> &geoCoords);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void coordinateChanged(const QPointF &newGeoCoord);
|
||||||
|
void invalidCoordinate();
|
||||||
|
private:
|
||||||
|
void rebuildUi();
|
||||||
|
void updateToolTip();
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onResetToDefaultCoordinate();
|
||||||
|
private:
|
||||||
|
bool m_bWithResetButton = false;
|
||||||
|
bool m_bShowToolTip = true;
|
||||||
|
bool m_bMgrsSquareEditable = false;
|
||||||
|
LtsCoordWidget::DisplayType m_displayType = LtsCoordWidget::DisplayType::MGRS;
|
||||||
|
LtsCoordInputWidgetHandler *m_inputWidgetHandler = nullptr;
|
||||||
|
QPointF m_coordinate;
|
||||||
|
QPointF m_defaultCoordinate;
|
||||||
|
};
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Mgrs</class>
|
||||||
|
<widget class="QWidget" name="Mgrs">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>322</width>
|
||||||
|
<height>32</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="cbxZone"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="leEast">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Pos X</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="leNorth">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Pos Y</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="tbReset">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>:/style/arrow_back.svg</normaloff>:/style/arrow_back.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
Loaded 100 of 11247 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user