Step 2 - LDF Loading: - ldfparser integration (Python) / custom regex parser (C++) - QTreeWidget with expandable signal rows, merged Value column - Hex/Dec toggle, FreeFormat schedule entries, auto-reload - Baud rate auto-detection from LDF Step 3 - Signal Editing: - Bit packing/unpacking (signal value ↔ frame bytes) - ReadOnlyColumnDelegate for per-column editability - Value clamping to signal width, recursion guard Step 4 - Rx Panel: - receive_rx_frame() API with timestamp, signal unpacking - Change highlighting (yellow), auto-scroll toggle, clear button - Dashboard view (in-place update per frame_id) Step 5 - Connection Panel: - ConnectionManager with state machine (Disconnected/Connecting/Connected/Error) - Port scanning (pyserial / QSerialPort), connect/disconnect with UI mapping Step 6 - BabyLIN Backend: - BabyLinBackend wrapping Lipowsky BabyLIN_library.py DLL - Mock mode for macOS/CI, device scan, SDF loading, signal access - Frame callbacks, raw command access Step 7 - Master Scheduler: - QTimer-based schedule execution with start/stop/pause - Frame sent callback with visual highlighting - Mock Rx simulation, manual send, global rate override Tests: Python 171 | C++ 124 (Steps 1-5 parity, Steps 6-7 Python-first) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
191 lines
7.0 KiB
CMake
191 lines
7.0 KiB
CMake
# CMakeLists.txt — Build configuration for the LIN Simulator (C++)
|
|
#
|
|
# CMAKE BASICS:
|
|
# =============
|
|
# CMake is a build system generator. It doesn't compile code directly.
|
|
# Instead, it generates platform-specific build files:
|
|
# - macOS/Linux: Makefiles (then you run `make`)
|
|
# - Windows: Visual Studio project files
|
|
# - Any platform: Ninja build files (faster than Make)
|
|
#
|
|
# The workflow is:
|
|
# 1. mkdir build && cd build
|
|
# 2. cmake .. ← generates build files from this CMakeLists.txt
|
|
# 3. cmake --build . ← compiles and links the project
|
|
# 4. ./lin_simulator ← run the application
|
|
#
|
|
# Qt6 integration:
|
|
# CMake has built-in support for Qt via find_package(Qt6).
|
|
# Qt needs special preprocessing for its features:
|
|
# - MOC (Meta-Object Compiler): processes Q_OBJECT macros for signals/slots
|
|
# - UIC: compiles .ui designer files to C++ headers (we don't use these)
|
|
# - RCC: compiles resource files (icons, etc.) into the binary
|
|
|
|
cmake_minimum_required(VERSION 3.16)
|
|
|
|
project(LINSimulator
|
|
VERSION 0.1.0
|
|
LANGUAGES CXX
|
|
DESCRIPTION "LIN Simulator using BabyLIN devices"
|
|
)
|
|
|
|
# C++17 standard — required for structured bindings, std::optional, etc.
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
|
|
# ── Qt6 auto-processing ──
|
|
# These three lines enable Qt's special preprocessors:
|
|
# AUTOMOC: automatically runs MOC on headers containing Q_OBJECT
|
|
# AUTOUIC: automatically compiles .ui files (if we had any)
|
|
# AUTORCC: automatically compiles .qrc resource files
|
|
set(CMAKE_AUTOMOC ON)
|
|
set(CMAKE_AUTOUIC ON)
|
|
set(CMAKE_AUTORCC ON)
|
|
|
|
# ── Find Qt6 ──
|
|
# find_package searches for Qt6 on the system.
|
|
# REQUIRED means CMake will error if Qt6 is not found.
|
|
# COMPONENTS lists which Qt modules we need:
|
|
# Widgets: GUI widgets (QMainWindow, QPushButton, etc.)
|
|
# We'll add SerialPort in Step 5 when we need device communication.
|
|
find_package(Qt6 REQUIRED COMPONENTS Widgets SerialPort)
|
|
|
|
# ── Main application target ──
|
|
# qt_add_executable is Qt's wrapper around add_executable.
|
|
# It handles platform-specific details (macOS app bundle, Windows subsystem, etc.)
|
|
qt_add_executable(lin_simulator
|
|
src/main.cpp
|
|
src/main_window.cpp
|
|
src/main_window.h
|
|
src/ldf_parser.cpp
|
|
src/ldf_parser.h
|
|
src/connection_manager.cpp
|
|
src/connection_manager.h
|
|
)
|
|
|
|
target_link_libraries(lin_simulator PRIVATE Qt6::Widgets Qt6::SerialPort)
|
|
|
|
# ── Tests ──
|
|
# We use Qt's built-in test framework (QTest) instead of GoogleTest
|
|
# because it integrates naturally with Qt's event loop and widgets.
|
|
# QTest provides:
|
|
# - QVERIFY(condition): assert a condition is true
|
|
# - QCOMPARE(actual, expected): assert two values are equal
|
|
# - QTest::mouseClick(): simulate mouse events
|
|
# - QTest::keyClick(): simulate keyboard events
|
|
enable_testing()
|
|
find_package(Qt6 REQUIRED COMPONENTS Test)
|
|
|
|
qt_add_executable(test_main_window
|
|
tests/test_main_window.cpp
|
|
src/main_window.cpp
|
|
src/main_window.h
|
|
src/ldf_parser.cpp
|
|
src/ldf_parser.h
|
|
src/connection_manager.cpp
|
|
src/connection_manager.h
|
|
)
|
|
|
|
target_link_libraries(test_main_window PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
|
|
target_include_directories(test_main_window PRIVATE src)
|
|
add_test(NAME test_main_window COMMAND test_main_window)
|
|
|
|
# ── Step 2: LDF Parser tests ──
|
|
# These test the custom LDF parser (ldf_parser.cpp) in isolation,
|
|
# without any GUI. C++ equivalent of python/tests/test_ldf_handler.py.
|
|
#
|
|
# COMPILE DEFINITIONS:
|
|
# target_compile_definitions() injects #define macros at compile time.
|
|
# We use it to pass the sample LDF file path to the test code:
|
|
#
|
|
# CMake: target_compile_definitions(... LDF_SAMPLE_PATH="/absolute/path/sample.ldf")
|
|
# Effect: #define LDF_SAMPLE_PATH "/absolute/path/sample.ldf" ← injected into source
|
|
# Usage: parseLdf(QString(LDF_SAMPLE_PATH)); ← in test code
|
|
#
|
|
# This is how C++ projects pass configuration to code at build time.
|
|
# Python doesn't need this because it resolves paths at runtime with __file__.
|
|
#
|
|
# CMAKE_CURRENT_SOURCE_DIR is a built-in CMake variable pointing to the
|
|
# directory containing this CMakeLists.txt file (/path/to/cpp/).
|
|
set(LDF_SAMPLE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../resources/sample.ldf")
|
|
|
|
# Parser unit tests — only needs ldf_parser, no main_window
|
|
qt_add_executable(test_ldf_parser
|
|
tests/test_ldf_parser.cpp
|
|
src/ldf_parser.cpp
|
|
src/ldf_parser.h
|
|
)
|
|
|
|
target_link_libraries(test_ldf_parser PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
|
|
target_include_directories(test_ldf_parser PRIVATE src)
|
|
target_compile_definitions(test_ldf_parser PRIVATE
|
|
LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
|
|
)
|
|
add_test(NAME test_ldf_parser COMMAND test_ldf_parser)
|
|
|
|
# ── Step 2: LDF Loading GUI integration tests ──
|
|
# These test the full pipeline: parse LDF → populate MainWindow tables.
|
|
# C++ equivalent of python/tests/test_ldf_loading.py.
|
|
#
|
|
# This target needs BOTH ldf_parser AND main_window because it tests
|
|
# how the GUI responds to loading an LDF file. Each test target in CMake
|
|
# is a separate executable — it needs all the source files it depends on.
|
|
#
|
|
# WHY DUPLICATE SOURCE FILES IN MULTIPLE TARGETS:
|
|
# Unlike Python where importing a module shares code, each C++ executable
|
|
# is compiled independently. test_ldf_loading needs main_window.cpp because
|
|
# it creates MainWindow objects. We could avoid this with a shared library,
|
|
# but for a small project, listing sources per target is simpler.
|
|
qt_add_executable(test_ldf_loading
|
|
tests/test_ldf_loading.cpp
|
|
src/main_window.cpp
|
|
src/main_window.h
|
|
src/ldf_parser.cpp
|
|
src/ldf_parser.h
|
|
src/connection_manager.cpp
|
|
src/connection_manager.h
|
|
)
|
|
|
|
target_link_libraries(test_ldf_loading PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
|
|
target_include_directories(test_ldf_loading PRIVATE src)
|
|
target_compile_definitions(test_ldf_loading PRIVATE
|
|
LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
|
|
)
|
|
add_test(NAME test_ldf_loading COMMAND test_ldf_loading)
|
|
|
|
# ── Step 3: Signal editing tests ──
|
|
qt_add_executable(test_signal_editing
|
|
tests/test_signal_editing.cpp
|
|
src/main_window.cpp
|
|
src/main_window.h
|
|
src/ldf_parser.cpp
|
|
src/ldf_parser.h
|
|
src/connection_manager.cpp
|
|
src/connection_manager.h
|
|
)
|
|
|
|
target_link_libraries(test_signal_editing PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
|
|
target_include_directories(test_signal_editing PRIVATE src)
|
|
target_compile_definitions(test_signal_editing PRIVATE
|
|
LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
|
|
)
|
|
add_test(NAME test_signal_editing COMMAND test_signal_editing)
|
|
|
|
# ── Step 4: Rx real-time display tests ──
|
|
qt_add_executable(test_rx_realtime
|
|
tests/test_rx_realtime.cpp
|
|
src/main_window.cpp
|
|
src/main_window.h
|
|
src/ldf_parser.cpp
|
|
src/ldf_parser.h
|
|
src/connection_manager.cpp
|
|
src/connection_manager.h
|
|
)
|
|
|
|
target_link_libraries(test_rx_realtime PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Test)
|
|
target_include_directories(test_rx_realtime PRIVATE src)
|
|
target_compile_definitions(test_rx_realtime PRIVATE
|
|
LDF_SAMPLE_PATH="${LDF_SAMPLE_PATH}"
|
|
)
|
|
add_test(NAME test_rx_realtime COMMAND test_rx_realtime)
|