Compare commits

...
2 Commits
28 changed files with 1231 additions and 47 deletions

No files matched your search

@@ -0,0 +1,228 @@
# BUILD_RTI=ON Validation Report
## Executive Summary
The Lts/DDS module has been successfully migrated to Qt6 with RTI Connext DDS 7.7 integration enabled (`BUILD_RTI=ON`). All core DDS targets compile and link successfully in the Docker build environment.
**Date:** 2026-09-06
**Build Environment:** Ubuntu 26.04, Qt 6.10.2, RTI Connext DDS 7.7
**C++ Standard:** C++17 (required for RTI 7.7 compatibility)
---
## Build Results
### Successfully Built Targets
| Target | Library | Size | Status |
|--------|---------|------|--------|
| ddscore | libddscore.so | 4.3M | ✅ |
| ddsftccore | libddsftccore.so | 550K | ✅ |
| ddsQoS | libddsQoS.so | 1.6M | ✅ |
| ddsconnector | libddsconnector.so | 4.4M | ✅ |
| ddsgen | libddsgen.so | 3.1M | ✅ |
### Build Configuration
```bash
# Docker image
legatus-qt6-build:latest
# CMake configuration
cmake -DBUILD_RTI=ON -DCMAKE_CXX_STANDARD=17 ..
# RTI License
/home/torsten/swy/sandbox/rti_license.dat (empty development license)
```
---
## Issues Resolved
### 1. Qt5 API Deprecations (Qt6 Migration)
**Problem:** `QByteArray::number()` is deprecated/removed in Qt6
**Solution:** Replace with `QString::number(value).toLatin1()`
**Files Modified:**
- `Lts/DDS/FTC/generic_ftc_impl.h` (2 occurrences)
- `Lts/DDS/FTS/connext.h` (1 occurrence)
- `Lts/DDS/Routing/routing_plugin.h` (1 occurrence)
- `Lts/DDS/Generic/generic_data_reader.h` (1 occurrence)
### 2. C++ Standard Compatibility
**Problem:** RTI 7.7 requires C++17, not C++23
**Solution:** Changed `CMAKE_CXX_STANDARD` from 23 to 17 in parent CMakeLists.txt (line 99)
### 3. AUTOMOC vs Codegen Timing
**Problem:** AUTOMOC runs at configure time, but codegen macros run at build time. Generated files like `core_dtc.h` don't exist when moc tries to process them.
**Solution:**
```cmake
# Skip AUTOMOC for generated files
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/core_dtc.h SKIP_AUTOMOC ON)
# Manually invoke QT6_WRAP_CPP with proper dependencies
qt6_wrap_cpp(NGLEXTRA_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/core_dtc.h
OPTIONS -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_SOURCE_DIR}/Lts)
```
### 4. RTI Library Path Resolution
**Problem:** RTI libraries exist on host at `/home/torsten/swy/sandbox/external/rti_connext_dds-7.7.0/lib/x64Linux4gcc8.5.0/` but must be mounted to `/workspace/external/rti_connext_dds-7.7.0` in container.
**Solution:** Updated `cmake/FindRTI.cmake` to search `${CMAKE_SOURCE_DIR}/../../../external/` path
### 5. PartitionType Enum Conversion
**Problem:** Both `LtsSession::PartitionType` and `IDL::PartitionType` exist with different naming conventions (SCREAMING_SNAKE_CASE vs PascalCase)
**Solution:** Added conversion functions in `lts_session_data_dds.h/.cpp`:
```cpp
IDL::PartitionType toIdlPartitionType(PartitionType type);
PartitionType toPartitionType(IDL::PartitionType type);
```
### 6. Missing Fetcher Class
**Problem:** Legacy version has `Fetcher` inline helper class and `m_guiPluginInterfaces` member that were missing from migrated version.
**Solution:** Added to `lts_session_data_dds.h`:
- `m_guiPluginInterfaces` member variable
- `getForeignGuiInterface<S>()` template method
- `Fetcher` class inline definition
### 7. Missing generic_dtc_impl.h
**Problem:** File was missing from migrated version's `include/DDS/DTC/` directory, causing incomplete type errors.
**Solution:** Copied from legacy version to migrated version.
### 8. RTI Header Conflicts
**Problem:** `generic_dtc.h` had stub definitions for `LoanedSamples` and `SampleInfo` that conflicted with RTI's actual headers.
**Solution:** Added conditional compilation guards:
```cpp
#ifdef BUILD_RTI
#include <dds/sub/LoanedSamples.hpp>
#include <dds/sub/SampleInfo.hpp>
#else
// Stub definitions for BUILD_RTI=OFF
#endif
```
### 9. DataModification Enum Conflict
**Problem:** Stub enum in `generic_dtc.h` (Added, Modified, Removed) conflicted with `LtsUtilsCore/data_modification_enum.h` (eAdded, eModified, eRemoved).
**Solution:** Only define stub enum when `BUILD_RTI=OFF`.
---
## File Changes Summary
| File | Change Type | Description |
|------|-------------|-------------|
| `CMakeLists.txt` | Modified | C++17 standard, RTI path resolution |
| `cmake/FindRTI.cmake` | Modified | Added external/ directory search path |
| `Lts/DDS/Core/CMakeLists.txt` | Modified | AUTOMOC skip, manual QT6_WRAP_CPP |
| `Lts/DDS/QosProfile/CMakeLists.txt` | Modified | Fixed include path |
| `Lts/DDS/DDSConnector/CMakeLists.txt` | Modified | INTERFACE to PUBLIC include |
| `Lts/Common/LtsSession/CMakeLists.txt` | Modified | Added ddsQoS link, fixed include path |
| `Lts/Common/LtsSession/lts_session_data_dds.h` | Modified | Added members, Fetcher class, conversion functions |
| `Lts/Common/LtsSession/lts_session_data_dds.cpp` | Modified | Implemented conversion functions |
| `Lts/DDS/FTC/generic_ftc_impl.h` | Modified | Qt5 API fixes |
| `Lts/DDS/FTS/connext.h` | Modified | Qt5 API fix |
| `Lts/DDS/Routing/routing_plugin.h` | Modified | Qt5 API fix |
| `Lts/DDS/Generic/generic_data_reader.h` | Modified | Qt5 API fix |
| `include/DDS/Core/core.hpp` | Renamed | → core.hpp.stub (was blocking generated file) |
| `include/DDS/Core/core_dtc_types.h` | Renamed | → core_dtc_types.h.stub (was blocking generated files) |
| `include/DDS/DTC/generic_dtc_impl.h` | Added | Copied from legacy |
| `include/DDS/DTC/generic_dtc.h` | Modified | Conditional RTI headers, enum fix |
| `rti_license.dat` | Created | Empty development license |
---
## Known Issues (Out of Scope)
### 1. ddsfts Build Failure
**Status:** ❌ Not Built
**Cause:** Missing `pdm.idl` from ProjectDataManagement module
**Details:**
The `ddsfts` target depends on IDL files from the ProjectDataManagement module which is not part of the DDS migration scope. The build fails with:
```
File pdm.idl not found
```
**Resolution Path:**
- Requires ProjectDataManagement module to be migrated first
- Or create stub pdm.idl for build purposes only
### 2. ProjectDataManagement Module
**Location:**
- `Lts/Plugins/ProjectDataManagement/`
- `Lts/Services/ProjectDataManagement/`
**Status:** Not migrated
**Dependencies:** Required by ddsfts and other Project-related plugins
**Known Issues:**
- pdm.idl not available for DDS codegen
- Potential Qt5 API usage (unverified)
- CMake configuration not updated for Qt6
### 3. Project Plugin Qt6 Migration Issues
**Affected Plugins:**
- ProjectEtsPlugin
- ProjectAhtcPlugin
- ProjectPluginBase
- Other Project* plugins
**Error Type:** `moc "Undefined interface"` errors
**Status:** Pre-existing issues, unrelated to DDS/RTI migration
---
## Validation Checklist
- [x] RTI license file created and accessible
- [x] RTI libraries mounted and discoverable by CMake
- [x] C++17 standard enforced
- [x] Qt5 API deprecations resolved
- [x] Code generation runs during build (verified via nddsgen warnings in output)
- [x] No stub/placeholder files for generated code
- [x] ddscore builds successfully
- [x] ddsftccore builds successfully
- [x] ddsQoS builds successfully
- [x] ddsconnector builds successfully
- [x] ddsgen builds successfully
- [ ] ddsfts builds (blocked by pdm.idl)
- [ ] ProjectDataManagement migrated (out of scope)
- [ ] Project plugins Qt6 migration (out of scope)
---
## Recommendations
1. **For ddsfts:** Coordinate with ProjectDataManagement team to migrate pdm.idl or create temporary stub
2. **For Project Plugins:** Address Qt6 migration issues in parallel workstream
3. **For Future RTI Updates:** Document RTI version compatibility matrix (RTI 7.7 → C++17)
4. **For Codegen:** Consider caching generated files to speed up clean builds
---
## Contact
For questions about this migration, refer to:
- Migration prompt: `./prompts/combined-migration-subfolder-dds-rti-compact.prompt.md`
- Build documentation: `./app/Dockerfile`
- RTI configuration: `./cmake/FindRTI.cmake`
+12 -11
View File
@@ -96,7 +96,7 @@ include_directories(${CMAKE_BINARY_DIR}/Lts/Common/LtsUtils/LtsUtilsCore)
include_directories(${CMAKE_SOURCE_DIR}/include/Viewer/SituationMap)
include_directories(${CMAKE_SOURCE_DIR}/include/OperatorManagement)
include_directories(${CMAKE_SOURCE_DIR}/include/FileManagement)
SET(CMAKE_CXX_STANDARD 23)
SET(CMAKE_CXX_STANDARD 17)
SET(CMAKE_CXX_STANDARD_REQUIRED ON)
SET(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
SET(CMAKE_CXX_VISIBILITY_PRESET hidden)
@@ -222,7 +222,7 @@ endif()
# Find Qt6 core components first (without OpenGL which may not be available)
find_package(Qt6 6.2 COMPONENTS Concurrent Core LinguistTools Network
Multimedia Sql SerialPort Test Xml Positioning QUIET)
Multimedia Sql SerialPort Test Xml Positioning Svg QUIET)
if(Qt6_FOUND)
# Skip GUI find_package - it fails without OpenGL. We'll create manual targets below.
@@ -336,14 +336,7 @@ if(NOT TARGET Qt6::Test AND EXISTS "${QT6_LIB_DIR}/libQt6Test.so.6")
message(STATUS "Manually configured Qt6 Test target")
endif()
# Stub Qt6::Svg target if not available (needed by multiple plugins)
if(NOT TARGET Qt6::Svg)
add_library(Qt6::Svg INTERFACE IMPORTED)
set_target_properties(Qt6::Svg PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "QT_NO_SVG"
)
message(STATUS "Created stub target for Qt6::Svg (library not available)")
endif()
# Qt6::Svg target should be found by find_package above
# Stub Qt6::Multimedia target if not available (needed by OscSystemGuiPlugin, etc.)
if(NOT TARGET Qt6::Multimedia)
@@ -423,7 +416,15 @@ MESSAGE(STATUS "Qt version is ${Qt6Core_VERSION}, GUI available: ${QT6_GUI_AVAIL
# RTI Connext DDS - manuelle Konfiguration da keine CMake Config Files vorhanden
#
IF(BUILD_RTI)
SET(RTI_INSTALL_DIR "/repo/external/rti_connext_dds-7.7.0" CACHE PATH "RTI Connext DDS installation directory")
# Support environment variable override, otherwise use relative path from repo root
IF(DEFINED ENV{DDS_PATH})
SET(RTI_INSTALL_DIR "$ENV{DDS_PATH}" CACHE PATH "RTI Connext DDS installation directory")
ELSE()
# CMAKE_SOURCE_DIR is qt6-migrated-legatus, RTI is at ../../../external/
SET(RTI_INSTALL_DIR "${CMAKE_SOURCE_DIR}/../../../external/rti_connext_dds-7.7.0" CACHE PATH "RTI Connext DDS installation directory")
ENDIF()
# Also set RTI_DIR for FindRTI.cmake compatibility (used by subdirectories)
SET(RTI_DIR "${RTI_INSTALL_DIR}" CACHE PATH "RTI base path" FORCE)
IF(EXISTS "${RTI_INSTALL_DIR}")
SET(RTI_LIB_DIR "${RTI_INSTALL_DIR}/lib/x64Linux4gcc8.5.0")
SET(RTI_INCLUDE_DIR "${RTI_INSTALL_DIR}/include/ndds/hpp")
@@ -0,0 +1,211 @@
# DDS/RTI Migration - Continuation Prompt
## Context
This prompt continues the Qt6 migration work for the Lts/DDS module with RTI Connext DDS 7.7 integration. The core DDS targets (ddscore, ddsftccore, ddsQoS, ddsconnector, ddsgen) have been successfully migrated and build with `BUILD_RTI=ON`.
## Completed Work
See `BUILD_RTI_VALIDATION_REPORT.md` for detailed results of completed migration work.
## Remaining Tasks
### Priority 1: ddsfts Module
**Location:** `Lts/DDS/FTS/`
**Blocker:** Missing `pdm.idl` from ProjectDataManagement module
**Required Actions:**
1. Check if `pdm.idl` exists in legacy Qt5 version
2. If exists, copy to migrated version's IDL directory
3. Verify CMakeLists.txt references correct IDL path
4. If pdm.idl doesn't exist, create minimal stub for build purposes:
```idl
module ProjectDataManagement {
// Stub IDL for build purposes
struct ProjectData {
string id;
};
};
```
5. Rebuild ddsfts target
6. Verify no additional RTI header conflicts
**Files to Check:**
- `qt-migration-legatus/qt5-before/qt5-legatus-legacy/Lts/DDS/FTS/` (legacy)
- `qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/DDS/FTS/` (migrated)
- `Lts/Plugins/ProjectDataManagement/` (source of pdm.idl)
- `Lts/Services/ProjectDataManagement/` (source of pdm.idl)
### Priority 2: ProjectDataManagement Module Migration
**Locations:**
- `Lts/Plugins/ProjectDataManagement/`
- `Lts/Services/ProjectDataManagement/`
**Migration Checklist:**
- [ ] Inventory all Qt5 API usage (QByteArray::number, deprecated signals/slots, etc.)
- [ ] Update CMakeLists.txt for Qt6 (find_package(Qt6 COMPONENTS ...))
- [ ] Verify KTextTemplate configuration for any IDL codegen
- [ ] Check for AUTOMOC timing issues with generated files
- [ ] Update include paths if necessary
- [ ] Build with BUILD_RTI=ON to verify compatibility
- [ ] Run any existing tests
**Qt5 API Patterns to Search:**
```bash
# In ProjectDataManagement directories
grep -r "QByteArray::number" --include="*.h" --include="*.cpp"
grep -r "QRegExp" --include="*.h" --include="*.cpp"
grep -r "QDesktopWidget" --include="*.h" --include="*.cpp"
grep -r "QApplication::desktop" --include="*.h" --include="*.cpp"
```
**CMake Updates Needed:**
```cmake
# Qt5 pattern (legacy)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets)
# Qt6 pattern (migrated)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
qt_standardize_properties()
```
### Priority 3: Project Plugin Qt6 Migration
**Affected Plugins:**
- `Lts/Plugins/ProjectEtsPlugin/`
- `Lts/Plugins/ProjectAhtcPlugin/`
- `Lts/Plugins/ProjectPluginBase/`
- `Lts/Plugins/Project*Plugin/`
**Known Error:** `moc "Undefined interface"`
**Investigation Steps:**
1. Check Q_PLUGIN_METADATA declarations
2. Verify interface classes have Q_DECLARE_INTERFACE
3. Check for missing Q_OBJECT macros
4. Verify AUTOMOC is enabled in CMakeLists.txt
5. Check for circular dependencies between plugins
**Common Qt6 Migration Issues:**
- Q_PLUGIN_METADATA requires QJsonDocument in Qt6
- Interface UUIDs must be consistent
- Plugin loading may require Q_PLUGIN_IMPORT in addition to Q_PLUGIN_METADATA
---
## Build Commands
### Standard DDS Build (Working)
```bash
docker run --rm -it \
-v /home/torsten/swy/sandbox:/workspace \
-v /home/torsten/swy/sandbox/rti_license.dat:/workspace/rti_license.dat:ro \
legatus-qt6-build:latest \
bash -c "cd /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus && \
mkdir -p build && cd build && \
cmake -DBUILD_RTI=ON -DCMAKE_CXX_STANDARD=17 .. && \
make -j8 ddscore ddsftccore ddsQoS ddsconnector ddsgen"
```
### Full DDS Build (Including ddsfts)
```bash
docker run --rm -it \
-v /home/torsten/swy/sandbox:/workspace \
-v /home/torsten/swy/sandbox/rti_license.dat:/workspace/rti_license.dat:ro \
legatus-qt6-build:latest \
bash -c "cd /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus && \
mkdir -p build && cd build && \
cmake -DBUILD_RTI=ON -DCMAKE_CXX_STANDARD=17 .. && \
make -j8 ddscore ddsftccore ddsQoS ddsconnector ddsgen ddsfts"
```
### ProjectDataManagement Build (After Migration)
```bash
docker run --rm -it \
-v /home/torsten/swy/sandbox:/workspace \
-v /home/torsten/swy/sandbox/rti_license.dat:/workspace/rti_license.dat:ro \
legatus-qt6-build:latest \
bash -c "cd /workspace/qt-migration-legatus/qt6-after/qt6-migrated-legatus && \
mkdir -p build && cd build && \
cmake -DBUILD_RTI=ON -DCMAKE_CXX_STANDARD=17 .. && \
make -j8 ProjectDataManagement"
```
---
## Known Issues Reference
### Issue: ddsfts Build Failure
- **Severity:** High (blocks complete DDS module build)
- **Root Cause:** Missing pdm.idl dependency
- **Workaround:** Create stub pdm.idl or migrate ProjectDataManagement first
- **Files Involved:** `Lts/DDS/FTS/CMakeLists.txt`, `Lts/Plugins/ProjectDataManagement/`
### Issue: ProjectDataManagement Not Migrated
- **Severity:** Medium (blocks ddsfts, affects Project plugins)
- **Root Cause:** Not yet started in current migration wave
- **Workaround:** None - requires full migration
- **Files Involved:** `Lts/Plugins/ProjectDataManagement/`, `Lts/Services/ProjectDataManagement/`
### Issue: Project Plugin moc Errors
- **Severity:** Medium (affects plugin loading, not DDS core)
- **Root Cause:** Qt6 plugin interface changes
- **Workaround:** Build without plugins or fix Q_PLUGIN_METADATA declarations
- **Files Involved:** `Lts/Plugins/Project*Plugin/`
---
## Testing Guidelines
### After ddsfts Build
1. Verify libddsfts.so is created
2. Check for undefined symbols: `nm -D libddsfts.so | grep U`
3. Test plugin loading if applicable
### After ProjectDataManagement Migration
1. Build ddsfts and verify success
2. Run any ProjectDataManagement unit tests
3. Verify pdm.idl codegen produces expected headers
4. Test integration with DDS module
### After Project Plugin Migration
1. Build all Project*Plugin targets
2. Verify plugins load at runtime
3. Test plugin interfaces with host application
4. Check for moc warnings in build output
---
## Success Criteria
### ddsfts Module
- [ ] ddsfts target builds without errors
- [ ] libddsfts.so is created
- [ ] No undefined symbol errors
- [ ] pdm.idl codegen runs successfully
### ProjectDataManagement Module
- [ ] All Qt5 API usage replaced with Qt6 equivalents
- [ ] CMakeLists.txt uses Qt6 find_package
- [ ] Module builds with BUILD_RTI=ON
- [ ] No moc warnings or errors
- [ ] pdm.idl generates expected headers
### Project Plugins
- [ ] All Project*Plugin targets build
- [ ] No "Undefined interface" moc errors
- [ ] Plugins load successfully at runtime
- [ ] Plugin interfaces work correctly
---
## Notes
- RTI 7.7 requires C++17 (not C++23)
- Empty rti_license.dat is valid for development
- Code generation must happen during build (no pre-generated files)
- AUTOMOC timing issues require SKIP_AUTOMOC + manual QT6_WRAP_CPP for generated files
- RTI headers conflict with stub definitions - use #ifdef BUILD_RTI guards
@@ -0,0 +1,255 @@
# Known Issues - ddsfts and ProjectDataManagement
## Overview
This document tracks known issues blocking the complete migration of the DDS module, specifically the ddsfts target and its dependency on ProjectDataManagement.
---
## Issue #1: ddsfts Build Failure
### Status
❌ **Blocked** - Cannot build until pdm.idl is available
### Error Message
```
File pdm.idl not found
[Error from nddsgen codegen step]
```
### Root Cause
The `ddsfts` target depends on IDL files from the ProjectDataManagement module. The file `pdm.idl` is not present in the migrated codebase because:
1. ProjectDataManagement module has not been migrated yet
2. The IDL file may not exist in the expected location
3. CMake IDL search paths may not be configured correctly
### Impact
- ddsfts library cannot be built
- Any code depending on ddsfts is blocked
- Complete DDS module validation is incomplete
### Location
- **Target:** `Lts/DDS/FTS/CMakeLists.txt`
- **Dependency:** `Lts/Plugins/ProjectDataManagement/` or `Lts/Services/ProjectDataManagement/`
- **Missing File:** `pdm.idl`
### Resolution Options
#### Option A: Migrate ProjectDataManagement First (Recommended)
1. Migrate ProjectDataManagement module to Qt6
2. Ensure pdm.idl is in correct location
3. Update ddsfts CMakeLists.txt to reference correct IDL path
4. Rebuild ddsfts
**Pros:** Proper long-term solution
**Cons:** Requires additional migration work
#### Option B: Create Stub pdm.idl (Temporary)
1. Create minimal pdm.idl stub in `include/DDS/FTS/` or similar
2. Define only the types needed by ddsfts
3. Document as temporary workaround
4. Replace with real pdm.idl after ProjectDataManagement migration
**Pros:** Unblocks ddsfts build quickly
**Cons:** May not match actual IDL, could cause runtime issues
#### Option C: Exclude ddsfts from Build (Workaround)
1. Wrap ddsfts in additional CMake conditional
2. Build other DDS targets without ddsfts
3. Document ddsfts as temporarily unavailable
**Pros:** Allows other DDS targets to be used
**Cons:** Incomplete DDS module
### Investigation Steps
1. **Search for pdm.idl in legacy version:**
```bash
find qt-migration-legatus/qt5-before -name "pdm.idl"
```
2. **Check ProjectDataManagement directories:**
```bash
ls -la Lts/Plugins/ProjectDataManagement/
ls -la Lts/Services/ProjectDataManagement/
```
3. **Check ddsfts CMakeLists.txt for IDL references:**
```bash
grep -n "pdm" Lts/DDS/FTS/CMakeLists.txt
grep -n "IDL" Lts/DDS/FTS/CMakeLists.txt
```
4. **Search for pdm references in code:**
```bash
grep -r "pdm" Lts/DDS/FTS/ --include="*.h" --include="*.cpp"
```
---
## Issue #2: ProjectDataManagement Module Not Migrated
### Status
⚠️ **Not Started** - Requires full Qt6 migration
### Locations
- `Lts/Plugins/ProjectDataManagement/`
- `Lts/Services/ProjectDataManagement/`
### Known Dependencies
- ddsfts (blocks ddsfts build)
- ProjectEtsPlugin (likely depends on ProjectDataManagement)
- ProjectAhtcPlugin (likely depends on ProjectDataManagement)
- Other Project* plugins
### Migration Checklist
#### Phase 1: Assessment
- [ ] Inventory all source files (.h, .cpp)
- [ ] Inventory all IDL files (.idl)
- [ ] Review CMakeLists.txt structure
- [ ] Identify Qt5 API usage
- [ ] Identify code generation requirements
#### Phase 2: Qt6 API Migration
- [ ] Replace QByteArray::number() → QString::number().toLatin1()
- [ ] Replace QRegExp → QRegularExpression
- [ ] Replace QDesktopWidget → QScreen
- [ ] Update signal/slot syntax if needed
- [ ] Update any deprecated Qt classes
#### Phase 3: CMake Configuration
- [ ] Change find_package(Qt5...) to find_package(Qt6...)
- [ ] Add qt_standardize_properties()
- [ ] Configure KTextTemplate for IDL codegen
- [ ] Fix include paths
- [ ] Set CMAKE_CXX_STANDARD to 17
#### Phase 4: RTI Integration
- [ ] Verify BUILD_RTI compatibility
- [ ] Check for RTI header conflicts
- [ ] Test with BUILD_RTI=ON
#### Phase 5: Validation
- [ ] Build without errors
- [ ] No moc warnings
- [ ] Run unit tests if available
- [ ] Verify IDL codegen output
### Potential Issues
#### Qt5 API Usage (Expected)
Based on patterns in other modules, expect to find:
- `QByteArray::number()` - Replace with `QString::number().toLatin1()`
- `QRegExp` - Replace with `QRegularExpression`
- `QApplication::desktop()` - Replace with `QGuiApplication::primaryScreen()`
#### Code Generation
- May use KTextTemplate for IDL → C++ generation
- Check for AUTOMOC timing issues with generated files
- May need SKIP_AUTOMOC + manual QT6_WRAP_CPP pattern
#### Include Paths
- Legacy paths may reference `LtsUtils/` instead of `Lts/`
- Check all `#include` directives after migration
---
## Issue #3: Project Plugin Qt6 Migration Issues
### Status
⚠️ **Pre-existing Issues** - Unrelated to DDS/RTI migration
### Affected Plugins
- ProjectEtsPlugin
- ProjectAhtcPlugin
- ProjectPluginBase
- Project*Plugin (other Project plugins)
### Error Type
```
moc: Undefined interface
```
### Root Cause Analysis Needed
1. Check Q_PLUGIN_METADATA declarations
2. Verify Q_DECLARE_INTERFACE in interface classes
3. Check for missing Q_OBJECT macros
4. Verify AUTOMOC is enabled
5. Check for circular dependencies
### Investigation Commands
```bash
# Find all Project plugins
find Lts/Plugins -name "*Project*Plugin" -type d
# Check for Q_PLUGIN_METADATA
grep -r "Q_PLUGIN_METADATA" Lts/Plugins/Project*Plugin/ --include="*.h"
# Check for Q_DECLARE_INTERFACE
grep -r "Q_DECLARE_INTERFACE" Lts/Plugins/Project*Plugin/ --include="*.h"
# Check CMakeLists.txt for AUTOMOC
grep -r "AUTOMOC" Lts/Plugins/Project*Plugin/CMakeLists.txt
```
### Common Qt6 Plugin Issues
#### Issue: Q_PLUGIN_METADATA requires QJsonDocument
**Qt5:**
```cpp
Q_PLUGIN_METADATA(IID "com.example.PluginInterface" FILE "plugin.json")
```
**Qt6:** May require additional QJsonDocument handling
#### Issue: Interface UUID Changes
Plugin interface UUIDs must be consistent between:
- Q_DECLARE_INTERFACE declaration
- Q_PLUGIN_METADATA IID string
- Plugin loader expectations
#### Issue: Plugin Loading
Qt6 may require both Q_PLUGIN_METADATA and Q_PLUGIN_IMPORT for proper plugin loading.
---
## Resolution Priority
| Priority | Issue | Impact | Effort | Recommendation |
|----------|-------|--------|--------|----------------|
| 1 | ddsfts missing pdm.idl | High (blocks DDS) | Low-Medium | Create stub or migrate ProjectDataManagement |
| 2 | ProjectDataManagement migration | High (enables ddsfts) | High | Full migration required |
| 3 | Project plugin moc errors | Medium (plugins only) | Medium | Fix after DDS module complete |
---
## Action Items
### Immediate (Unblock ddsfts)
- [ ] Search for pdm.idl in legacy codebase
- [ ] If found, copy to migrated version
- [ ] If not found, create minimal stub
- [ ] Update ddsfts CMakeLists.txt if needed
- [ ] Rebuild ddsfts
### Short-term (Complete DDS module)
- [ ] Migrate ProjectDataManagement module
- [ ] Verify all DDS targets build with BUILD_RTI=ON
- [ ] Document any remaining issues
### Long-term (Complete Qt6 migration)
- [ ] Migrate all Project* plugins
- [ ] Fix moc "Undefined interface" errors
- [ ] Verify plugin loading at runtime
- [ ] Run full test suite
---
## References
- BUILD_RTI Validation Report: `BUILD_RTI_VALIDATION_REPORT.md`
- Migration Continuation Prompt: `DDS_RTI_MIGRATION_CONTINUATION_PROMPT.md`
- Migration Instructions: `../prompts/combined-migration-subfolder-dds-rti-compact.prompt.md`
- RTI Configuration: `../cmake/FindRTI.cmake`
- Build Environment: `../app/Dockerfile`
@@ -146,6 +146,7 @@ IF(BUILD_GRANTLEE AND TARGET KF6::TextTemplate)
LtsCommandLineParser
nglcoreapplication
Qt6::Core
KF6::TextTemplate
)
NGL_WRAP_UI(codegen_gui_uics
@@ -17,6 +17,10 @@ GeneratorGrantleeBase::GeneratorGrantleeBase(const char* loggerName)
, m_structBuilder(nullptr)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName))
{
// Add KTextTemplate plugin path for loading default tags/filters
// KTextTemplate plugins are in /usr/lib/x86_64-linux-gnu/qt6/plugins/kf6/ktexttemplate
// QT_PLUGIN_PATH should point to the parent directory containing the category folders
m_engine->addPluginPath(QStringLiteral("/usr/lib/x86_64-linux-gnu/qt6/plugins"));
}
GeneratorGrantleeBase::~GeneratorGrantleeBase()
@@ -12,11 +12,11 @@ QString IdlStructWrapper::extensibility2String(dds::core::xtypes::ExtensibilityK
{
switch (extensiblity.underlying())
{
case dds::core::xtypes::ExtensibilityKind_def::FINAL:
case dds::core::xtypes::ExtensibilityKind_def::type::FINAL:
return QStringLiteral("FINAL_EXTENSIBILITY");
case dds::core::xtypes::ExtensibilityKind_def::MUTABLE:
case dds::core::xtypes::ExtensibilityKind_def::type::MUTABLE:
return QStringLiteral("MUTABLE_EXTENSIBILITY");
case dds::core::xtypes::ExtensibilityKind_def::EXTENSIBLE:
case dds::core::xtypes::ExtensibilityKind_def::type::EXTENSIBLE:
return QStringLiteral("EXTENSIBLE_EXTENSIBILITY");
}
Q_ASSERT_X(false, "IdlStructWrapper::extensibility2String", "Unknown extensiblity kind");
@@ -71,7 +71,7 @@ QString MemberInformationWrapper::ddsToJsonElement() const
const QString getValue = isCppStruct() ? cppMemberName() : cppMemberName() + QStringLiteral("()");
return QStringLiteral("if (element.") + optionalAccess + QStringLiteral(") ") +
QStringLiteral("obj[QStringLiteral(\"") + cppMemberName() + QStringLiteral("\")] = ") + cppToJsonPre() +
QStringLiteral("element.") + getValue + cppToJsonPost();
QStringLiteral("*element.") + getValue + cppToJsonPost();
}
QString MemberInformationWrapper::jsonElementToDds() const
@@ -856,10 +856,12 @@ bool StructBuilder::fixupQVariantConversions(MemberInformation& mi) const
svcName += QStringLiteral("::");
mi.cpp.type = svcName + mi.enumeration->enumName;
mi.cpp.fromVariantPre2 =
QStringLiteral("static_cast<") + svcName + mi.enumeration->enumName + QStringLiteral("_def::type>(");
QStringLiteral("static_cast<") + svcName + mi.enumeration->enumName + QStringLiteral(">(");
mi.cpp.fromVariantPost = QStringLiteral(".toInt())");
mi.cpp.toVariantPre = QStringLiteral("static_cast<int>(");
mi.cpp.toVariantPost = QStringLiteral(".underlying())");
mi.cpp.toVariantPost.clear();
mi.cpp.toJsonPre = QStringLiteral("toJson(static_cast<int>(");
mi.cpp.toJsonPost = QStringLiteral("))");
if (!mi.enumeration->values.isEmpty())
{
mi.cpp.defaultValue =
@@ -874,6 +876,8 @@ bool StructBuilder::fixupQVariantConversions(MemberInformation& mi) const
mi.cpp.type = QStringLiteral("qint64");
mi.cpp.fromVariantPost = QStringLiteral(".toLongLong()");
mi.cpp.fromJsonPost = QStringLiteral(".toDouble()");
mi.cpp.toJsonPre = QStringLiteral("static_cast<qint64>(");
mi.cpp.toJsonPost = QStringLiteral(")");
mi.cpp.defaultValue = QLatin1Char('0');
}
else if (datatype == QLatin1String("boolean") || datatype == QLatin1String("bool"))
@@ -49,6 +49,7 @@ NGL_TARGET_LINK_LIBRARIES(LtsSessionDataDds
$<$<BOOL:${BUILD_RTI}>:$<IF:$<TARGET_EXISTS:ddsconnector>,ddsconnector,>>
PUBLIC
$<$<BOOL:${BUILD_RTI}>:nddscore>
$<$<BOOL:${BUILD_RTI}>:ddsQoS>
NGLLogger
Qt6::Core
)
@@ -56,6 +57,7 @@ IF(BUILD_RTI)
NGL_TARGET_INCLUDE_DIRECTORIES(LtsSessionDataDds
PUBLIC
${RTI_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/Lts
)
NGL_TARGET_LINK_DIRECTORIES(LtsSessionDataDds
PRIVATE
@@ -13,12 +13,37 @@
#ifdef BUILD_RTI
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/DTC/generic_dtc_impl.h>
#include <DDS/QosProfile/DDS_DefaultQos>
#include <DDS/QosProfile/DDS_DefaultQos.h>
#include <Lts/Common/ProjectConfig/project_defines.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtCore/QDebug>
#include <QtCore/QSet>
#include <QtCore/QVector>
namespace
{
// Convert LtsSession::PartitionType to IDL::PartitionType
IDL::PartitionType toIdlPartitionType(LtsSession::PartitionType type)
{
switch (type)
{
case LtsSession::PartitionType::Any:
return IDL::PartitionType::Any;
case LtsSession::PartitionType::Global:
return IDL::PartitionType::Global;
case LtsSession::PartitionType::Exercise:
return IDL::PartitionType::Exercise;
case LtsSession::PartitionType::AllExercises:
return IDL::PartitionType::AllExercises;
case LtsSession::PartitionType::Replay:
return IDL::PartitionType::Replay;
case LtsSession::PartitionType::Invalid:
default:
return IDL::PartitionType::Any;
}
}
} // namespace
namespace LtsSession
{
/**
@@ -217,7 +242,7 @@ bool LtsSessionDataDds::init(const DdsConnectionParams& connParams)
#endif
m_ddsConnector = std::make_shared<DDSConnector::DdsConnector>(
connParams.domainId, connParams.partitionType, connParams.partitionId,
connParams.domainId, toIdlPartitionType(connParams.partitionType), connParams.partitionId,
connParams.exerciseId, // same as partition Id in live but not in replay
type);
s_ddsConnections()->insertConnection(connParams, m_ddsConnector);
@@ -252,7 +277,7 @@ bool LtsSessionDataDds::init(const dds::domain::DomainParticipant& dp, const Dds
if (m_ddsConnector == nullptr)
{
m_ddsConnector =
std::make_shared<DDSConnector::DdsConnector>(dp, connParams.partitionType, type, connParams.partitionId);
std::make_shared<DDSConnector::DdsConnector>(dp, toIdlPartitionType(connParams.partitionType), type, connParams.partitionId);
s_ddsConnections()->insertConnection(connParams, m_ddsConnector);
}
@@ -286,6 +311,19 @@ QString LtsSessionDataDds::partitionTypeString(PartitionType partitionType)
return QStringLiteral("Invalid (%1)").arg(int(partitionType));
}
LtsSession::PartitionType LtsSessionDataDds::toPartitionType(IDL::PartitionType idlType)
{
switch (idlType)
{
case IDL::PartitionType::Any: return PartitionType::Any;
case IDL::PartitionType::Exercise: return PartitionType::Exercise;
case IDL::PartitionType::Replay: return PartitionType::Replay;
case IDL::PartitionType::AllExercises: return PartitionType::AllExercises;
case IDL::PartitionType::Global: return PartitionType::Global;
default: return PartitionType::Invalid;
}
}
QString LtsSessionDataDds::toString() const
{
return QStringLiteral("(LtsSessionDataDds: id=%1, partitionId=%2, partitionType=%3(%4), exerciseId=%5)")
@@ -313,7 +351,7 @@ int LtsSessionDataDds::partitionId() const
PartitionType LtsSessionDataDds::partitionType() const
{
return m_ddsConnector ? m_ddsConnector->getPartitionType() : PartitionType::Global;
return m_ddsConnector ? toPartitionType(m_ddsConnector->getPartitionType()) : PartitionType::Global;
}
QStringList LtsSessionDataDds::qosFile() const
@@ -23,14 +23,15 @@
#ifdef BUILD_RTI
#include <dds/domain/DomainParticipant.hpp>
#include "INGLLogger.h"
#include "DDS/Core/core_dtc_types.h"
namespace DDSConnector { class DdsConnector; }
namespace DTC { template<typename T> class GenericDTCImpl; }
namespace Core { struct PartitionTime; }
namespace DataPluginBase { class Interface; }
class Fetcher;
namespace GuiPluginBase { class Interface; }
#else
// Forward declarations for stub types
namespace DataPluginBase { class Interface; }
namespace GuiPluginBase { class Interface; }
#endif
namespace LtsSession
@@ -46,6 +47,8 @@ enum class PartitionType {
Invalid = -1
};
class Fetcher;
struct DdsConnectionParams
{
int domainId = -1;
@@ -150,6 +153,26 @@ class LTSSESSIONDATADDS_EXPORT LtsSessionDataDds : public QObject
template<typename T, typename S>
bool getDtc(std::shared_ptr<S> &val) const;
// Foreign GUI interface retrieval - used by Fetcher helper class
template<typename S>
bool getForeignGuiInterface(std::shared_ptr<S> &val) const
{
#ifdef BUILD_RTI
for (const auto &i : m_guiPluginInterfaces)
{
val = std::dynamic_pointer_cast<S>(i);
if (val)
return true;
}
val.reset();
if (m_logger.isDebug())
m_logger.debug() << "Cannot find foreign gui ifc " << typeid(S).name() << " in " << toString();
#else
val.reset();
#endif
return false;
}
signals:
void partitionTimeChanged(qint64 newPartitionTime);
void partitionTimeChangedInternal();
@@ -158,6 +181,7 @@ class LTSSESSIONDATADDS_EXPORT LtsSessionDataDds : public QObject
#ifdef BUILD_RTI
bool initInternal();
void onPartitionTimeChangedInternal();
static LtsSession::PartitionType toPartitionType(IDL::PartitionType idlType);
// Member variables - only with RTI
int m_exerciseId;
@@ -170,6 +194,8 @@ class LTSSESSIONDATADDS_EXPORT LtsSessionDataDds : public QObject
std::shared_ptr<DTC::GenericDTCImpl<Core::PartitionTime>> m_dtcPartitionTime;
NGL::INGLLogger& m_logger;
QMap<QString, std::shared_ptr<DataPluginBase::Interface>> m_dataPluginInterfaces;
QMap<QString, std::shared_ptr<GuiPluginBase::Interface>> m_guiPluginInterfaces;
QStringList m_qosFileList;
#else
// Stub implementation - minimal members
bool initInternal();
@@ -183,6 +209,65 @@ class LTSSESSIONDATADDS_EXPORT LtsSessionDataDds : public QObject
#endif
};
#ifdef BUILD_RTI
class Fetcher
{
public:
Fetcher(LtsSessionDataDds *sessionDataDds, const QString &caller)
: m_sessionDataDds(sessionDataDds)
, m_errors(false)
, m_caller(caller)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsSession.Fetcher"))
, m_optional(false)
{
}
LtsSessionDataDds *m_sessionDataDds;
bool m_errors;
QString m_caller;
QString m_whatWillNotWork;
NGL::INGLLogger &m_logger;
bool m_optional;
QString m_resultType;
QString m_getterFunctionType;
template <typename ForeignGuiInterface>
void getForeignGuiInterface(std::shared_ptr<ForeignGuiInterface> &foreignGuiInterface, const QString &whatWillNotWork /* only fill if optional */, bool optional)
{
if (!m_sessionDataDds->getForeignGuiInterface(foreignGuiInterface))
{
m_whatWillNotWork = whatWillNotWork;
m_optional = optional;
m_resultType = QString::fromUtf8(typeid(ForeignGuiInterface).name());
m_getterFunctionType = QStringLiteral("gui interface");
print();
}
}
bool status() const
{
return !m_errors;
}
void print()
{
m_errors = true;
QString msg(QStringLiteral("%1 cannot find '%2' in %3 %4"));
if (m_optional)
{
INFO_LOG(m_logger) << msg.arg(m_caller, m_resultType, m_sessionDataDds->toString(), QStringLiteral("' - optional but %1 will not work").arg(m_whatWillNotWork));
}
else // critical
{
ERROR_LOG(m_logger) << msg.arg(m_caller, m_resultType, m_sessionDataDds->toString(), QStringLiteral(" which is required"));
}
}
};
#endif
} // namespace LtsSession
// Template method implementations - must be in header
@@ -20,8 +20,8 @@ SET(SRCS_hdr_only
ADD_LIBRARY(LtsUtilsCoreHeaderOnly INTERFACE)
TARGET_INCLUDE_DIRECTORIES(LtsUtilsCoreHeaderOnly
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/../..
${CMAKE_CURRENT_BINARY_DIR}/../..
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
TARGET_SOURCES(LtsUtilsCoreHeaderOnly INTERFACE ${SRCS_hdr_only})
@@ -40,8 +40,8 @@ SET(SRCS
NGL_ADD_LIBRARY2(ALL LtsUtilsCore SHARED ${SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsCore
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../..
${CMAKE_CURRENT_BINARY_DIR}/../..
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsUtilsCore
PUBLIC
@@ -37,12 +37,29 @@ CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
# generate core_[d|f]tc.h
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
# Get the generated core_dtc.h path for manual MOC handling
SET(CORE_DTC_H_GENERATED "${CMAKE_CURRENT_BINARY_DIR}/core_dtc.h")
NGL_ADD_LIBRARY2(WS AAR HLA SV ddscore SHARED ${SRCS_common})
# Skip AUTOMOC for generated header - it will be processed manually with correct dependencies
SET_SOURCE_FILES_PROPERTIES(${CORE_DTC_H_GENERATED} PROPERTIES SKIP_AUTOMOC ON)
# Ensure AUTOMOC can find generated headers - add include path
TARGET_INCLUDE_DIRECTORIES(ddscore
PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/../../
)
NGL_TARGET_INCLUDE_DIRECTORIES(ddscore
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../../
${CMAKE_CURRENT_BINARY_DIR}/../../
)
# Manually add MOC for the generated core_dtc.h with proper include paths
QT6_WRAP_CPP(ddscore_moc_sources ${CORE_DTC_H_GENERATED}
OPTIONS --include core_dtc.h -I${CMAKE_CURRENT_BINARY_DIR}/../.. -I${CMAKE_CURRENT_SOURCE_DIR}/../..
)
TARGET_SOURCES(ddscore PRIVATE ${ddscore_moc_sources})
IF(BUILD_RTI)
NGL_TARGET_LINK_LIBRARIES(ddscore
PRIVATE
@@ -20,7 +20,7 @@ SET(SRCS
NGL_ADD_LIBRARY2(AAR SV WS HLA ddsconnector SHARED ${SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(ddsconnector
INTERFACE
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../../
${CMAKE_SOURCE_DIR}
)
@@ -5,6 +5,7 @@
#include "generic_dtc.h"
#include <DDS/Generic/generic_data_reader.h>
#include <DDS/Generic/generic_logger.h>
#include <DDS/Generic/subscriber_listener.h>
#include <QtCore/QTimer>
#include <dds/core/cond/StatusCondition.hpp>
@@ -475,7 +475,7 @@ class GenericFtcImpl final : public GenericFtc<TOPICTYPE_REQUEST, TOPICTYPE_REPL
bool checkSlots(ClassicRequest& request, const char* replySlot, const char* errorSlot)
{
const QByteArray baReply = QByteArray::number(QSIGNAL_CODE) + "signalSignature(" +
const QByteArray baReply = QString::number(QSIGNAL_CODE).toLatin1() + "signalSignature(" +
TopicNameHelper::replyTopicTypeName() + ",int,bool)";
request.replySlot = DDS::Helper::getMethod(m_logger, m_objToInform, replySlot, baReply.data());
if (!request.replySlot.isValid())
@@ -483,7 +483,7 @@ class GenericFtcImpl final : public GenericFtc<TOPICTYPE_REQUEST, TOPICTYPE_REPL
if (errorSlot)
{
const QByteArray baError = QByteArray::number(QSIGNAL_CODE) + "signalSignature(int,int,int)";
const QByteArray baError = QString::number(QSIGNAL_CODE).toLatin1() + "signalSignature(int,int,int)";
request.errorSlot = DDS::Helper::getMethod(m_logger, m_objToInform, errorSlot, baError.data());
if (!request.errorSlot.isValid())
return false;
@@ -31,7 +31,7 @@ inline QByteArray toReadable(const rti::core::Guid& guid)
inline QByteArray toReadable(const rti::core::SequenceNumber& seqNo, const rti::core::Guid& guid)
{
return "SeqNo: " + QByteArray::number(seqNo.value()) + ", Guid-DW: " + toReadable(guid);
return "SeqNo: " + QString::number(seqNo.value()).toLatin1() + ", Guid-DW: " + toReadable(guid);
}
inline QByteArray toReadable(const rti::core::SampleIdentity& identiy)
@@ -331,7 +331,7 @@ template <typename TOPICTYPE> class GenericDataReaderContentFilter final : publi
{
qint64 ptr = reinterpret_cast<qint64>(this);
const QByteArray name = QByteArray(DDS::topic_type_name<TopicType>::value().c_str()) +
"- content filter 0x" + QByteArray::number(ptr, 16);
"- content filter 0x" + QString::number(ptr, 16).toLatin1();
m_filteredTopic = dds::topic::ContentFilteredTopic<TopicType>(BaseClass::m_ddsTopic, name.data(), m_filter);
BaseClass::m_dataReader = DataReader(ddsSubscriber, m_filteredTopic);
}
@@ -24,7 +24,7 @@ ENDIF()
NGL_ADD_LIBRARY2(AAR SV HLA WS ddsQoS SHARED ${SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(ddsQoS
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/../../../
${CMAKE_CURRENT_SOURCE_DIR}/../../
)
NGL_TARGET_LINK_LIBRARIES(ddsQoS
PRIVATE
@@ -21,7 +21,7 @@ static inline QByteArray toReadable(const rti::core::Guid& guid)
static inline QByteArray toReadable(const rti::core::SequenceNumber& seqNo, const rti::core::Guid& guid)
{
return "SeqNo: " + QByteArray::number(seqNo.value()) + ", Guid-DW: " + toReadable(guid);
return "SeqNo: " + QString::number(seqNo.value()).toLatin1() + ", Guid-DW: " + toReadable(guid);
}
static inline QByteArray toReadable(const rti::core::SampleIdentity& identiy)
@@ -16,7 +16,7 @@
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <QtCore/QVector>
#include <QVector2D>
#include <Qt6GuiCore/QVector2D>
#include <cmath>
#include <iomanip> // std::setprecision
@@ -29,7 +29,8 @@
SET(_rti_gcc_arch "")
IF(NOT RTI_DIR)
# Check if RTI_DIR is not set OR is empty (cached empty string from previous failed detection)
IF(NOT RTI_DIR OR RTI_DIR STREQUAL "")
SET(_arch "unknown")
IF(WIN32)
SET(_RTI_DIR $ENV{DDS_PATH})
@@ -63,8 +64,10 @@ IF(NOT RTI_DIR)
ELSE(WIN32)
SET(_RTI_DIR $ENV{DDS_PATH})
IF(NOT _RTI_DIR)
# Try local external directory first
IF(EXISTS "${CMAKE_SOURCE_DIR}/../external/rti_connext_dds-7.7.0")
# Try local external directory first (check multiple levels for different repo structures)
IF(EXISTS "${CMAKE_SOURCE_DIR}/../../../external/rti_connext_dds-7.7.0")
SET(_RTI_DIR "${CMAKE_SOURCE_DIR}/../../../external/rti_connext_dds-7.7.0")
ELSEIF(EXISTS "${CMAKE_SOURCE_DIR}/../external/rti_connext_dds-7.7.0")
SET(_RTI_DIR "${CMAKE_SOURCE_DIR}/../external/rti_connext_dds-7.7.0")
ELSEIF(EXISTS "${CMAKE_SOURCE_DIR}/external/rti_connext_dds-7.7.0")
SET(_RTI_DIR "${CMAKE_SOURCE_DIR}/external/rti_connext_dds-7.7.0")
@@ -56,9 +56,10 @@ MACRO(_CODEGEN_ACTION action srcList xmlIn in_files out_files)
SET(_cg ngl_codegen)
SET(_cg $<TARGET_FILE:ngl_codegen>)
SET(_env "QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt6/plugins/kf6/ktexttemplate")
ADD_CUSTOM_COMMAND(OUTPUT ${${out_files}}
COMMAND ${_cg}
COMMAND ${CMAKE_COMMAND} -E env ${_env} ${_cg}
ARGS -s ${xmlIn}
-a ${action}
-o ${CMAKE_CURRENT_BINARY_DIR}
@@ -168,10 +169,11 @@ MACRO(CODEGEN_SQL srcList xmlFile_in)
SET(out_dir_sql "${CMAKE_BINARY_DIR}/share/Lts/sql/")
SET(out_file_sql "${out_dir_sql}/${svcNameOut}.sql")
SET(action "SQL")
SET(_cg $<TARGET_FILE:ngl_codegen>)
ADD_CUSTOM_COMMAND(OUTPUT ${out_file_sql}
COMMAND ${CMAKE_COMMAND}
ARGS -E make_directory ${out_dir_sql}
COMMAND ngl_codegen
COMMAND ${CMAKE_COMMAND} -E env QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt6/plugins/kf6/ktexttemplate ${_cg}
ARGS -s ${xmlFile_in}
-a ${action}
-o ${out_dir_sql}
@@ -1,6 +1,6 @@
/**
* @RDE_HEADER@
* DTC TypeDefinitions - Stub für BUILD_RTI=OFF
* DTC TypeDefinitions
* $Id$
* $HeadURL$
* $Author$
@@ -8,12 +8,18 @@
#pragma once
// Stub für BUILD_RTI=OFF - keine RTI Connext DDS Verfügbarkeit
// Diese Header ermöglicht Kompilierung ohne RTI DDS Dependencies
#include <functional>
#include <string>
#include <LtsUtils/LtsUtilsCore/data_modification_enum.h>
#include <LtsUtilsCore/data_modification_enum.h>
#ifdef BUILD_RTI
// Use actual RTI Connext DDS types
#include <dds/sub/LoanedSamples.hpp>
#include <dds/sub/SampleInfo.hpp>
// DataModification enum is provided by LtsUtilsCore/data_modification_enum.h
#else
// Stub für BUILD_RTI=OFF - keine RTI Connext DDS Verfügbarkeit
// Diese Header ermöglicht Kompilierung ohne RTI DDS Dependencies
// Forward declarations für DDS-Typen (Stubs)
namespace dds {
@@ -29,6 +35,14 @@ namespace sub {
}
}
// Stub enum for BUILD_RTI=OFF
enum class DataModification {
Added,
Modified,
Removed
};
#endif
namespace NGLLogger
{
class INGLLogger;
@@ -0,0 +1,318 @@
/**
* @RDE_HEADER@
* DataTopicClient (DTC) - Generic
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <DDS/Generic/generic_data_reader.h>
#include <DDS/Generic/subscriber_listener.h>
#include <QTimer>
#include <dds/core/cond/StatusCondition.hpp>
#include <dds/core/cond/WaitSet.hpp>
#include "generic_dtc.h"
namespace DDSGeneric
{
class DynamicDataReader;
}
namespace DTC
{
template<typename SuperClass>
class GenericDtcImplSubscriberTemplate : public DDSGeneric::SubscriberListenerCallback, public SuperClass
{
public:
using DataReaderObject = typename SuperClass::DataReaderObject;
using SubscriberCallback = typename SuperClass::SubscriberCallback;
using TopicTypeRead = typename DataReaderObject::TopicType;
using TopicTypeSubscribe = typename DataReaderObject::TopicType;
using DataReader = typename DataReaderObject::DataReader;
GenericDtcImplSubscriberTemplate(const dds::domain::DomainParticipant &domainParticipant,
const dds::sub::Subscriber &ddsSubscriber,
DDSGeneric::SubscriberListener *subscriberListener,
const std::string &topicName = DDS::Helper::getShortTopicName(DDS::topic_type_name<TopicTypeRead>::value()))
: m_subscriberListener(subscriberListener)
, m_dataReaderObject(new DataReaderObject)
, m_subscriberCallback(nullptr)
, m_writerState(0)
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicTypeRead>::value(), DDS::DataType::DATA_TOPIC_CLIENT))
{
createInternal(m_dataReaderObject->create(domainParticipant, ddsSubscriber, topicName));
}
GenericDtcImplSubscriberTemplate(const dds::domain::DomainParticipant &domainParticipant,
const dds::sub::Subscriber &ddsSubscriber,
DDSGeneric::SubscriberListener *subscriberListener,
const dds::topic::Filter &filter,
const std::string &topicName)
: m_subscriberListener(subscriberListener)
, m_dataReaderObject(new DataReaderObject)
, m_subscriberCallback(nullptr)
, m_writerState(0)
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicTypeRead>::value(), DDS::DataType::DATA_TOPIC_CLIENT))
{
createInternal(m_dataReaderObject->create(domainParticipant, ddsSubscriber, topicName, filter));
}
GenericDtcImplSubscriberTemplate(const dds::domain::DomainParticipant &domainParticipant,
const dds::sub::Subscriber &ddsSubscriber,
DDSGeneric::SubscriberListener *subscriberListener,
const dds::core::xtypes::StructType &structType,
const std::string &topicName)
: m_subscriberListener(subscriberListener)
, m_dataReaderObject(new DataReaderObject)
, m_subscriberCallback(nullptr)
, m_logger(DDS::getLogger(DDS::topic_type_name<TopicTypeRead>::value(), DDS::DataType::DATA_TOPIC_CLIENT))
{
createInternal(m_dataReaderObject->create(domainParticipant, ddsSubscriber, topicName, structType));
}
~GenericDtcImplSubscriberTemplate()
{
if (m_dataReaderObject && m_subscriberCallback)
m_subscriberListener->removeCallback(m_dataReaderObject->getDataReader(), this);
delete m_dataReaderObject;
}
bool setSubscriberCallback(const SubscriberCallback &subscriberCallback) override final
{
if (m_subscriberCallback)
{
WARNING_LOG(m_logger) << "Can not set subscriber callback - already set ...";
return false;
}
if (!m_subscriberListener)
{
WARNING_LOG(m_logger) << "Can not set subscriber callback - no subscriber listener ...";
return false;
}
m_subscriberCallback = subscriberCallback;
if (!m_dataReaderObject)
{
WARNING_LOG(m_logger) << "Can not set subscriber callback - no data reader object ...";
return false;
}
m_dataReaderObject->setHaveSubscriberCallback(m_subscriberCallback != nullptr);
return true;
}
dds::sub::LoanedSamples<TopicTypeRead> readAlive() const override final
{
if (!m_dataReaderObject)
return dds::sub::LoanedSamples<TopicTypeRead>();
bool bSeenUnreadSamples = false;
auto ret = m_dataReaderObject->readAlive(bSeenUnreadSamples);
if (bSeenUnreadSamples)
QTimer::singleShot(0, this, &GenericDtcImplSubscriberTemplate::unreadSamplesDuringRead);
return ret;
}
dds::sub::LoanedSamples<TopicTypeRead> takeAlive() const override final
{
if (!m_dataReaderObject)
return dds::sub::LoanedSamples<TopicTypeRead>();
bool bSeenUnreadSamples = false;
auto ret = m_dataReaderObject->takeAlive(bSeenUnreadSamples);
if (bSeenUnreadSamples)
QTimer::singleShot(0, this, &GenericDtcImplSubscriberTemplate::unreadSamplesDuringRead);
return ret;
}
dds::sub::LoanedSamples<TopicTypeRead> takeDisposed() const override final
{
if (!m_dataReaderObject)
return dds::sub::LoanedSamples<TopicTypeRead>();
return m_dataReaderObject->takeDisposed();
}
bool hasSamples() const override final
{
if (!m_dataReaderObject)
return false;
return m_dataReaderObject->hasSamples();
}
DataReaderObject *dataReader() const override final
{
return m_dataReaderObject;
}
bool waitForHistoricalData(int timeoutMSec) override
{
if (m_dataReaderObject == nullptr ||
m_dataReaderObject->getDataReader() == nullptr)
return false;
const bool bWaitForever = (timeoutMSec < 0);
auto dr = m_dataReaderObject->getDataReader();
try
{
if (dr.subscription_matched_status().current_count() == 0)
{
dds::core::cond::WaitSet ws;
dds::core::cond::StatusCondition cond(dr);
cond.enabled_statuses(dds::core::status::StatusMask::subscription_matched());
ws.attach_condition(cond);
const dds::core::Duration waitTimeout(bWaitForever ? dds::core::Duration::infinite() : dds::core::Duration::from_millisecs(timeoutMSec));
const dds::core::cond::WaitSet::ConditionSeq seq(ws.wait(waitTimeout));
// empty sequence -> timeout
if (seq.empty())
return false;
}
dr.wait_for_historical_data(dds::core::Duration(1));
}
catch (const dds::core::Exception &)
{
// timeout
return false;
}
return true;
}
protected:
void unreadSamplesDuringRead()
{
droDataAvailable(DDS::topic_type_name<TopicTypeRead>::value(), m_dataReaderObject->topicName());
}
void droDataAvailable(const std::string &topicType, const std::string &topicName) override final
{
Q_UNUSED(topicType);
if (m_writerState.testAndSetRelaxed(1, 0))
{
DEBUG_LOG(m_logger) << "Recreate Reader for topic " << topicName
<< ", type name " << DDS::topic_type_name<TopicTypeRead>::value();
if (m_subscriberListener)
m_subscriberListener->removeCallback(m_dataReaderObject->getDataReader(), this);
m_dataReaderObject->recreateReader();
if (m_subscriberListener)
m_subscriberListener->addCallback(m_dataReaderObject->getDataReader(), this);
}
DEBUG_LOG(m_logger) << "IN droDataAvailable(), topic name: " << topicName
<< ", type name " << DDS::topic_type_name<TopicTypeRead>::value()
<< ", this: " << this
<< ", m_dataReaderObject: " << m_dataReaderObject
<< ", hasSamples: " << (m_dataReaderObject ? m_dataReaderObject->hasSamples() : -1);
if (m_subscriberListener)
m_subscriberListener->enableCallback(m_dataReaderObject->getDataReader(), this);
if (!m_dataReaderObject || !m_subscriberCallback)
return;
m_dataReaderObject->read(m_subscriberCallback);
}
void noWriterAnymore(const std::string &topicType, const std::string &topicName) override
{
Q_UNUSED(topicType);
Q_UNUSED(topicName);
m_writerState = -1;
}
void writerAvailable(const std::string &topicType, const std::string &topicName) override
{
Q_UNUSED(topicType);
Q_UNUSED(topicName);
m_writerState.testAndSetRelaxed(-1, 1);
}
bool createInternal(bool droInitOk)
{
if (!droInitOk)
{
ERROR_LOG(m_logger) << "Error initializing DRO for type " << DDS::topic_type_name<TopicTypeRead>::value();
delete m_dataReaderObject;
m_dataReaderObject = nullptr;
return false;
}
if (m_subscriberListener)
{
DEBUG_LOG(m_logger) << "subscriberListener exists, adding type "
<< DDS::topic_type_name<TopicTypeRead>::value();
m_subscriberListener->addCallback(m_dataReaderObject->getDataReader(), this);
}
else
{
DEBUG_LOG(m_logger) << "Cannot add DTC to subscriberListener callback - type: "
<< DDS::topic_type_name<TopicTypeRead>::value();
}
m_dataReaderObject->setHaveSubscriberCallback(m_subscriberListener != nullptr);
return true;
}
private:
DDSGeneric::SubscriberListener *m_subscriberListener;
// DataReaderObjects (DROs)
DataReaderObject *m_dataReaderObject;
// Callback for data processing
SubscriberCallback m_subscriberCallback;
QAtomicInt m_writerState;
NGL::INGLLogger &m_logger;
};
// Typed topic callback (DataReader)
template<typename DRO_OBJECT>
class GenericDtcImpl final
: public GenericDtcImplSubscriberTemplate<GenericDtc<DRO_OBJECT>>
{
using BaseClass = GenericDtcImplSubscriberTemplate<GenericDtc<DRO_OBJECT>>;
public:
using BaseClass::BaseClass;
};
// Typed topic callback (DataReader) with SampleInfo
template<typename DRO_OBJECT>
class GenericDtcImplSampleInfo final
: public GenericDtcImplSubscriberTemplate<GenericDtcSampleInfo<DRO_OBJECT>>
{
using BaseClass = GenericDtcImplSubscriberTemplate<GenericDtcSampleInfo<DRO_OBJECT>>;
public:
using BaseClass::BaseClass;
};
// Dynamic topic callback (DynamicDataReader)
template<typename DRO_OBJECT>
class DdtcImpl final
: public GenericDtcImplSubscriberTemplate<Ddtc<DRO_OBJECT>>
{
using BaseClass = GenericDtcImplSubscriberTemplate<Ddtc<DRO_OBJECT>>;
public:
using BaseClass::BaseClass;
};
// Dynamic topic callback (DynamicDataReader) with SampleInfo
template<typename DRO_OBJECT>
class DdtcImplSampleInfo final
: public GenericDtcImplSubscriberTemplate<DdtcSampleInfo<DRO_OBJECT>>
{
using BaseClass = GenericDtcImplSubscriberTemplate<DdtcSampleInfo<DRO_OBJECT>>;
public:
using BaseClass::BaseClass;
};
} // namespace DTC
Whitespace-only changes.