diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aee9fb3..d5958ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,34 +8,47 @@ on: - main jobs: + dbc-package-structure: + # Every directory containing a .dbc must be a ROS package (have package.xml + CMakeLists.txt calling dbc_package()) + name: DBC package structure + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check every DBC directory is a ROS package + run: | + status=0 + while IFS= read -r dir; do + if [ ! -f "$dir/package.xml" ] || [ ! -f "$dir/CMakeLists.txt" ]; then + echo "::error file=$dir::'$dir' contains .dbc file(s) but is not a ROS package (missing package.xml and/or CMakeLists.txt); colcon will silently skip it." + status=1 + fi + done < <(git ls-files '*.dbc' | xargs -r -n1 dirname | sort -u) + if [ "$status" -eq 0 ]; then + echo "OK: every directory with a .dbc is a ROS package." + fi + exit "$status" + build_and_test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - include: - - ros: humble - ubuntu: jammy - - ros: jazzy - ubuntu: noble - - ros: kilted - ubuntu: noble - - ros: lyrical - ubuntu: resolute - - ros: rolling - ubuntu: resolute + ros: [humble, jazzy, lyrical, rolling] name: ROS 2 ${{ matrix.ros }} container: - image: ghcr.io/ros-tooling/setup-ros-docker/setup-ros-docker-ubuntu-${{ matrix.ubuntu }}:latest + image: polymathrobotics/ros:${{ matrix.ros }}-builder-ubuntu env: - ROS_DISTRO: ${{ matrix.ros }} PIP_BREAK_SYSTEM_PACKAGES: 1 steps: - uses: actions/checkout@v4 - uses: ros-tooling/action-ros-ci@v0.4 with: target-ros2-distro: ${{ matrix.ros }} + # dbc_gen_cpp's own test suite, pulled in with its checkout; not ours to build. + colcon-extra-args: --packages-skip test_dbc_gen_cpp vcs-repo-file-url: repos.yaml + coverage-result: false + import-token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/upload-artifact@v4 with: name: colcon-logs-${{ matrix.ros }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index eb5f9a0..7756265 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -1,7 +1,7 @@ --- name: pre-commit -on: # yamllint disable-line +on: pull_request: push: branches: [main] diff --git a/.github/workflows/validate-dbc.yml b/.github/workflows/validate-dbc.yml new file mode 100644 index 0000000..568b220 --- /dev/null +++ b/.github/workflows/validate-dbc.yml @@ -0,0 +1,63 @@ +--- +name: DBC File Check + +on: + push: + branches: [main] + paths: + - '**.dbc' + - .github/workflows/validate-dbc.yml + pull_request: + paths: + - '**.dbc' + - .github/workflows/validate-dbc.yml + +jobs: + discover: + runs-on: ubuntu-latest + outputs: + dirs: ${{ steps.dirs.outputs.dirs }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Detect changed DBC files + id: filter + uses: dorny/paths-filter@v4 + with: + list-files: shell + filters: | + dbc: + - '**.dbc' + + - name: Reduce changed files to their directories + id: dirs + env: + DBC_CHANGED: ${{ steps.filter.outputs.dbc }} + DBC_FILES: ${{ steps.filter.outputs.dbc_files }} + run: | + if [ "$DBC_CHANGED" = 'true' ]; then + dirs="$(for f in $DBC_FILES; do dirname "$f"; done | sort -u | jq -R . | jq -sc .)" + else + dirs='[]' + fi + echo "dirs=$dirs" >> "$GITHUB_OUTPUT" + echo "Directories to validate: $dirs" + + validate-dbc: + needs: discover + if: needs.discover.outputs.dirs != '[]' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dir: ${{ fromJson(needs.discover.outputs.dirs) }} + name: validate-dbc (${{ matrix.dir }}) + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Validate DBC files + uses: polymathrobotics/validate_dbc@v0.1 + with: + path: ${{ matrix.dir }} diff --git a/repos.yaml b/repos.yaml index 16fbeb3..69906fd 100644 --- a/repos.yaml +++ b/repos.yaml @@ -1,6 +1,10 @@ --- repositories: + dbc_gen_cpp: + type: git + url: git@github.com:polymathrobotics/dbc_gen_cpp.git + version: main socketcan_adapter: type: git - url: https://github.com/polymathrobotics/socketcan_adapter.git + url: git@github.com:polymathrobotics/socketcan_adapter.git version: main diff --git a/sygnal_dbc/CMakeLists.txt b/sygnal_dbc/CMakeLists.txt deleted file mode 100644 index d18f137..0000000 --- a/sygnal_dbc/CMakeLists.txt +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -cmake_minimum_required(VERSION 3.5) - -project(sygnal_dbc VERSION 0.1.0 LANGUAGES C CXX) - -find_package(ament_cmake_auto REQUIRED) -ament_auto_find_build_dependencies() -find_package(Python3 COMPONENTS Interpreter REQUIRED) -include(GNUInstallDirs) - -# Install the DBC database directory -install( - DIRECTORY database/ - DESTINATION share/${PROJECT_NAME}/database -) - - -# Generate C sources/headers from all .dbc files under database/ -file(GLOB_RECURSE DBC_FILES CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/database/*.dbc") - -set(GENERATED_C_SOURCES) -set(GENERATED_HEADERS) - -foreach(DBC_FILE IN LISTS DBC_FILES) - # Compute path pieces to mirror the database/ layout under build dir - file(RELATIVE_PATH REL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/database" "${DBC_FILE}") - get_filename_component(REL_DIR "${REL_PATH}" DIRECTORY) - get_filename_component(DBC_BASENAME_WE "${DBC_FILE}" NAME_WE) - - # cantools generates lowercase file basenames; mirror that here - string(TOLOWER "${DBC_BASENAME_WE}" MOD_NAME) - - set(DB_ID "${REL_DIR}/${MOD_NAME}") - string(REGEX REPLACE "[^A-Za-z0-9_]" "_" DB_NAME "${DB_ID}") - string(TOLOWER "${DB_NAME}" DB_NAME) - - # Generate directly into the build include tree so dependents - # can include headers during the same colcon invocation. - set(OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/include/${PROJECT_NAME}") - set(OUT_C "${OUT_DIR}/${DB_NAME}.c") - set(OUT_H "${OUT_DIR}/${DB_NAME}.h") - - add_custom_command( - OUTPUT ${OUT_C} ${OUT_H} - COMMAND ${CMAKE_COMMAND} -E make_directory ${OUT_DIR} - COMMAND ${Python3_EXECUTABLE} -m cantools generate_c_source ${DBC_FILE} -o ${OUT_DIR} --database-name ${DB_NAME} - DEPENDS ${DBC_FILE} - COMMENT "Generating C source from DBC '${REL_PATH}' with cantools" - VERBATIM - ) - - list(APPEND GENERATED_C_SOURCES ${OUT_C}) - list(APPEND GENERATED_HEADERS ${OUT_H}) -endforeach() - -add_library(${PROJECT_NAME} STATIC ${GENERATED_C_SOURCES}) -target_include_directories(${PROJECT_NAME} - PUBLIC - $ - $ -) - -# Ensure generation runs as part of normal build (sources already bind it) -add_custom_target(generate_can_sources ALL - DEPENDS ${GENERATED_C_SOURCES} ${GENERATED_HEADERS}) - -# Install generated headers so dependents can include them -install( - DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/${PROJECT_NAME}/ - DESTINATION include/${PROJECT_NAME} - FILES_MATCHING PATTERN "*.h" -) - -install( - TARGETS ${PROJECT_NAME} - EXPORT export_${PROJECT_NAME} - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin -) - -ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET) -ament_package(CONFIG_EXTRAS "cmake/${PROJECT_NAME}-extras.cmake") - -if(BUILD_TESTING) - ament_auto_find_test_dependencies() - find_package(Catch2 REQUIRED) - - ament_add_pytest_test(test_mcm_heartbeat_python - test/test_mcm_heartbeat.py - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) - - add_executable(test_mcm_heartbeat_cpp test/test_mcm_heartbeat.cpp) - target_link_libraries(test_mcm_heartbeat_cpp PRIVATE Catch2::Catch2WithMain ${PROJECT_NAME}) - ament_add_test( - test_mcm_heartbeat_cpp - GENERATE_RESULT_FOR_RETURN_CODE_ZERO - COMMAND "$" - -r junit -s - -o test_results/${PROJECT_NAME}/test_mcm_heartbeat_cpp_output.xml - ENV CATCH_CONFIG_CONSOLE_WIDTH=120 - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - ) - -endif() diff --git a/sygnal_dbc/README.md b/sygnal_dbc/README.md index 681c5c3..a960510 100644 --- a/sygnal_dbc/README.md +++ b/sygnal_dbc/README.md @@ -1,62 +1,95 @@ # sygnal_dbc -Provides Sygnal DBC files and a C library produced by cantools. The package -installs the raw DBC database and, at build time, generates `.c/.h` files for -each `.dbc` and exposes them via a static library for consumers. - -## Contents -- Installed DBCs: `share/sygnal_dbc/database/...` -- Installed headers: `include/sygnal_dbc/_.h` - - Example: - - DBC Location `share/sygnal_dbc/database/mcm/Heartbeat.dbc` - - Header Location `include/sygnal_dbc/mcm_heartbeat.h` -- Library: `sygnal_dbc` -- CMake var: `SYGNAL_DBC_DIR` → points to installed `database` directory +Sygnal DBC databases and the generated C/C++ CAN message types built from them. -## Build -Prereqs: -- ROS 2 ament environment sourced -- Python 3 with cantools: `python3 -m pip install cantools` +Each subsystem directory is its own `ament_cmake` package that generates and exports +idiomatic C/C++ CAN message types from the DBC files it contains, using `dbc_gen_cpp`: -Build only this package: +| Directory | Package | Library prefix | +|----------------|---------------------------|-----------------| +| `mcm/` | `dbc_sygnal_mcm` | `mcm_` | +| `io/` | `dbc_sygnal_io` | `io_` | +| `cb/` | `dbc_sygnal_cb` | `cb_` | +| `cb_niro_dcan/`| `dbc_sygnal_cb_niro_dcan` | `cb_niro_dcan_` | +| `hpo/` | `dbc_sygnal_hpo` | `hpo_` | +| `vehicles/` | `dbc_sygnal_vehicles` | `vehicles_` | +| `sygnal_dbc/` | `sygnal_dbc` | — (umbrella) | -``` -colcon build --packages-select sygnal_dbc -``` +## How generation works + +Each package's `CMakeLists.txt` is a thin stub calling the shared `dbc_package()` helper +(`cmake/dbc_package.cmake`). The helper globs every `*.dbc` in the directory and produces +one exported library per file, so **adding a new DBC needs no CMake changes**. + +The library — and its C++ namespace, C-symbol prefix and header directory — is +`_`: + +- `mcm/Control.dbc` → library `mcm_control`, headers `mcm_control/mcm_control.{h,hpp}`, + namespace `mcm_control`, C symbols `mcm_control_*`. + +The `PREFIX` is required because stems repeat across subsystems: `Control.dbc`, +`Heartbeat.dbc`, `Identify.dbc`, `Configuration.dbc`, `Error.dbc` and `Commissioning.dbc` +each exist in `cb/`, `hpo/`, `io/` **and** `mcm/`. Naming libraries after the bare stem +would give four libraries called `Control`, all defining `control_*` symbols and all +installing `include/Control/Control.hpp` — a duplicate-symbol error for anything linking +two of them. + +`cb_niro_dcan/` is a sibling directory rather than `cb/niro_dcan/` because colcon does not +descend into a directory it has already identified as a package. The installed +`database/` tree still presents it at `cb/niro_dcan/`. ## Using in C++ (ament_cmake) -In your package CMakeLists.txt: -``` -find_package(ament_cmake REQUIRED) -find_package(sygnal_dbc REQUIRED) +Add the subsystem package(s) you need as a `` in your `package.xml`, then link the +generated libraries by name: -add_executable(my_node src/my_node.cpp) -ament_target_dependencies(my_node ) -# Link the library (headers are exported) -target_link_libraries(my_node sygnal_dbc) +```cmake +find_package(ament_cmake_auto REQUIRED) +ament_auto_find_build_dependencies() # finds dbc_sygnal_mcm (a depend) + +# Generated libs are exported as namespaced imported targets: ::. +target_link_libraries(my_node PRIVATE dbc_sygnal_mcm::mcm_control dbc_sygnal_mcm::mcm_relay) ``` -Include headers (lowercase file basenames, folder layout mirrors `database/`): +```cpp +#include "mcm_control/mcm_control.hpp" // C++ API +mcm_control::ControlCommand msg; -``` -#include -#include +extern "C" { +#include "mcm_control/mcm_control.h" // C API +} ``` -The exact header names come from each DBC file name, lowercased. +## Backwards compatibility + +This package previously built a single aggregate library named `sygnal_dbc` and installed +every generated header as `sygnal_dbc/_.h`. Both still work: + +- the `sygnal_dbc` package remains, as an INTERFACE library linking every generated + library, so `sygnal_dbc`, `ament_target_dependencies( sygnal_dbc)` + and `target_link_libraries( sygnal_dbc)` all keep resolving; +- forwarding headers at `include/sygnal_dbc/.{h,hpp}` include the real + `/.{h,hpp}`. + +The generated **C symbols are unchanged** across the split: the old build passed +`--database-name _` to cantools, which is exactly what the library names are +now. Existing consumers therefore need no changes at all. New code should include +`"/.hpp"` and link `dbc_sygnal_::` directly — the C++ API is new, +the old aggregate library only ever exposed the C one. ## Accessing raw DBCs -After `find_package(sygnal_dbc)`, the CMake variable `SYGNAL_DBC_DIR` is set to -`/share/sygnal_dbc/database`. Use it to locate raw `.dbc` files: -``` -# Example: install a copy of a specific dbc next to your target +`find_package(sygnal_dbc)` sets `SYGNAL_DBC_DIR` to `/share/sygnal_dbc/database`, +with the original `/.dbc` layout: + +```cmake install(FILES "${SYGNAL_DBC_DIR}/cb/Configuration.dbc" DESTINATION share/${PROJECT_NAME}/dbc) ``` -## Notes -- The installed headers incorporate the folder structure but to prevent conflicts add prefixes `database_` - (`cb_`, `io_`, `mcm_`, `vehicles_`). -- If you add or rename `.dbc` files, just rebuild; sources regenerate - automatically. +Each subsystem package also installs its own DBCs under `share//database`. + +## Build + +``` +colcon build --packages-up-to sygnal_dbc +``` diff --git a/sygnal_dbc/cb/CMakeLists.txt b/sygnal_dbc/cb/CMakeLists.txt new file mode 100644 index 0000000..3a10e91 --- /dev/null +++ b/sygnal_dbc/cb/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.8) + +project(dbc_sygnal_cb VERSION 0.1.0 LANGUAGES C CXX) + +# Auto-generate + export a CAN message library for every *.dbc in this directory. +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) +dbc_package(PREFIX cb) diff --git a/sygnal_dbc/database/cb/Commissioning.dbc b/sygnal_dbc/cb/Commissioning.dbc similarity index 100% rename from sygnal_dbc/database/cb/Commissioning.dbc rename to sygnal_dbc/cb/Commissioning.dbc diff --git a/sygnal_dbc/database/cb/Configuration.dbc b/sygnal_dbc/cb/Configuration.dbc similarity index 100% rename from sygnal_dbc/database/cb/Configuration.dbc rename to sygnal_dbc/cb/Configuration.dbc diff --git a/sygnal_dbc/database/cb/Control.dbc b/sygnal_dbc/cb/Control.dbc similarity index 100% rename from sygnal_dbc/database/cb/Control.dbc rename to sygnal_dbc/cb/Control.dbc diff --git a/sygnal_dbc/database/cb/Error.dbc b/sygnal_dbc/cb/Error.dbc similarity index 100% rename from sygnal_dbc/database/cb/Error.dbc rename to sygnal_dbc/cb/Error.dbc diff --git a/sygnal_dbc/database/cb/Heartbeat.dbc b/sygnal_dbc/cb/Heartbeat.dbc similarity index 100% rename from sygnal_dbc/database/cb/Heartbeat.dbc rename to sygnal_dbc/cb/Heartbeat.dbc diff --git a/sygnal_dbc/database/cb/Identify.dbc b/sygnal_dbc/cb/Identify.dbc similarity index 100% rename from sygnal_dbc/database/cb/Identify.dbc rename to sygnal_dbc/cb/Identify.dbc diff --git a/sygnal_dbc/cb/package.xml b/sygnal_dbc/cb/package.xml new file mode 100644 index 0000000..06fed64 --- /dev/null +++ b/sygnal_dbc/cb/package.xml @@ -0,0 +1,17 @@ + + + + dbc_sygnal_cb + 0.1.0 + Generated C/C++ CAN message types for the Sygnal CAN Bridge (CB) DBCs. Libraries are named cb_<dbc>. + Polymath Engineering + Apache-2.0 + + ament_cmake + + dbc_gen_cpp + + + ament_cmake + + diff --git a/sygnal_dbc/cb_niro_dcan/CMakeLists.txt b/sygnal_dbc/cb_niro_dcan/CMakeLists.txt new file mode 100644 index 0000000..c71ed06 --- /dev/null +++ b/sygnal_dbc/cb_niro_dcan/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.8) + +project(dbc_sygnal_cb_niro_dcan VERSION 0.1.0 LANGUAGES C CXX) + +# Auto-generate + export a CAN message library for every *.dbc in this directory. +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) +dbc_package(PREFIX cb_niro_dcan) diff --git a/sygnal_dbc/database/cb/niro_dcan/batterySOC.dbc b/sygnal_dbc/cb_niro_dcan/batterySOC.dbc similarity index 100% rename from sygnal_dbc/database/cb/niro_dcan/batterySOC.dbc rename to sygnal_dbc/cb_niro_dcan/batterySOC.dbc diff --git a/sygnal_dbc/database/cb/niro_dcan/door_seatbelt_state.dbc b/sygnal_dbc/cb_niro_dcan/door_seatbelt_state.dbc similarity index 100% rename from sygnal_dbc/database/cb/niro_dcan/door_seatbelt_state.dbc rename to sygnal_dbc/cb_niro_dcan/door_seatbelt_state.dbc diff --git a/sygnal_dbc/database/cb/niro_dcan/dtc.dbc b/sygnal_dbc/cb_niro_dcan/dtc.dbc similarity index 100% rename from sygnal_dbc/database/cb/niro_dcan/dtc.dbc rename to sygnal_dbc/cb_niro_dcan/dtc.dbc diff --git a/sygnal_dbc/cb_niro_dcan/package.xml b/sygnal_dbc/cb_niro_dcan/package.xml new file mode 100644 index 0000000..4d20691 --- /dev/null +++ b/sygnal_dbc/cb_niro_dcan/package.xml @@ -0,0 +1,17 @@ + + + + dbc_sygnal_cb_niro_dcan + 0.1.0 + Generated C/C++ CAN message types for the Kia Niro D-CAN vehicle bus read through the Sygnal CAN Bridge. Libraries are named cb_niro_dcan_<dbc>. + Polymath Engineering + Apache-2.0 + + ament_cmake + + dbc_gen_cpp + + + ament_cmake + + diff --git a/sygnal_dbc/database/cb/niro_dcan/tpms.dbc b/sygnal_dbc/cb_niro_dcan/tpms.dbc similarity index 100% rename from sygnal_dbc/database/cb/niro_dcan/tpms.dbc rename to sygnal_dbc/cb_niro_dcan/tpms.dbc diff --git a/sygnal_dbc/cmake/dbc_package.cmake b/sygnal_dbc/cmake/dbc_package.cmake new file mode 100644 index 0000000..e4462b4 --- /dev/null +++ b/sygnal_dbc/cmake/dbc_package.cmake @@ -0,0 +1,84 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Shared helper for the per-subsystem DBC packages in this directory. +# +# dbc_package() turns every *.dbc in the calling package's source directory into an +# exported C/C++ CAN message library, installs the raw DBCs, then finishes the ament +# package, so adding a new .dbc needs NO cmake changes. Each CMakeLists.txt is just: +# +# cmake_minimum_required(VERSION 3.8) +# project(dbc_sygnal_mcm) +# include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) +# dbc_package(PREFIX mcm) +# +# PREFIX is required to keep identical stems in sibling subsystems apart, since the +# library name is also the C symbol prefix, C++ namespace and header directory: +# +# mcm/Control.dbc -> mcm_control/mcm_control.hpp, namespace + symbols mcm_control +# +# This matches what the old cantools --database-name produced, so C symbols are +# unchanged across the migration. + +# The single definition of how a DBC file maps to a library name. Used by dbc_package() +# and by the sygnal_dbc umbrella to emit matching compatibility headers. +function(dbc_library_name out_var prefix dbc_path) + get_filename_component(_stem "${dbc_path}" NAME_WE) + # cantools lowercases generated basenames; match it so capitalisation of the DBC + # filename cannot shift the C symbol prefix. + string(TOLOWER "${_stem}" _stem) + set(${out_var} "${prefix}_${_stem}" PARENT_SCOPE) +endfunction() + +macro(dbc_package) + cmake_parse_arguments(_dbc_pkg "" "PREFIX" "" ${ARGN}) + + if(NOT _dbc_pkg_PREFIX) + message(FATAL_ERROR "dbc_package(${PROJECT_NAME}): PREFIX is required; it keeps " + "identically-named DBCs in sibling subsystems from colliding.") + endif() + + if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) + endif() + + find_package(ament_cmake REQUIRED) + # Provides the generate_dbc_cpp() macro. + find_package(dbc_gen_cpp REQUIRED) + + # CONFIGURE_DEPENDS re-globs on rebuild, so a newly added .dbc is picked up + # without a manual re-configure. + file(GLOB _dbc_files CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.dbc") + + if(NOT _dbc_files) + message(FATAL_ERROR "dbc_package(${PROJECT_NAME}): no .dbc files found in ${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + foreach(_dbc ${_dbc_files}) + dbc_library_name(_lib_name "${_dbc_pkg_PREFIX}" "${_dbc}") + + if(NOT _lib_name MATCHES "^[a-z_][a-z0-9_]*$") + message(FATAL_ERROR + "dbc_package(${PROJECT_NAME}): '${_lib_name}' (from ${_dbc}) is not a valid lowercase C/C++ " + "identifier. Rename the DBC file to a snake_case name not starting with a digit.") + endif() + + # Installs + exports itself, so downstream links ${PROJECT_NAME}::. + generate_dbc_cpp(${_lib_name} DBC ${_dbc}) + endforeach() + + # Keep the raw DBCs available at runtime, as the single sygnal_dbc package used to. + install(FILES ${_dbc_files} DESTINATION share/${PROJECT_NAME}/database) + + ament_package() +endmacro() diff --git a/sygnal_dbc/hpo/CMakeLists.txt b/sygnal_dbc/hpo/CMakeLists.txt new file mode 100644 index 0000000..23dfd18 --- /dev/null +++ b/sygnal_dbc/hpo/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.8) + +project(dbc_sygnal_hpo VERSION 0.1.0 LANGUAGES C CXX) + +# Auto-generate + export a CAN message library for every *.dbc in this directory. +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) +dbc_package(PREFIX hpo) diff --git a/sygnal_dbc/database/hpo/Commissioning.dbc b/sygnal_dbc/hpo/Commissioning.dbc similarity index 100% rename from sygnal_dbc/database/hpo/Commissioning.dbc rename to sygnal_dbc/hpo/Commissioning.dbc diff --git a/sygnal_dbc/database/hpo/Configuration.dbc b/sygnal_dbc/hpo/Configuration.dbc similarity index 100% rename from sygnal_dbc/database/hpo/Configuration.dbc rename to sygnal_dbc/hpo/Configuration.dbc diff --git a/sygnal_dbc/database/hpo/Control.dbc b/sygnal_dbc/hpo/Control.dbc similarity index 100% rename from sygnal_dbc/database/hpo/Control.dbc rename to sygnal_dbc/hpo/Control.dbc diff --git a/sygnal_dbc/database/hpo/Error.dbc b/sygnal_dbc/hpo/Error.dbc similarity index 100% rename from sygnal_dbc/database/hpo/Error.dbc rename to sygnal_dbc/hpo/Error.dbc diff --git a/sygnal_dbc/database/hpo/Heartbeat.dbc b/sygnal_dbc/hpo/Heartbeat.dbc similarity index 100% rename from sygnal_dbc/database/hpo/Heartbeat.dbc rename to sygnal_dbc/hpo/Heartbeat.dbc diff --git a/sygnal_dbc/database/hpo/Identify.dbc b/sygnal_dbc/hpo/Identify.dbc similarity index 100% rename from sygnal_dbc/database/hpo/Identify.dbc rename to sygnal_dbc/hpo/Identify.dbc diff --git a/sygnal_dbc/hpo/package.xml b/sygnal_dbc/hpo/package.xml new file mode 100644 index 0000000..cafd631 --- /dev/null +++ b/sygnal_dbc/hpo/package.xml @@ -0,0 +1,17 @@ + + + + dbc_sygnal_hpo + 0.1.0 + Generated C/C++ CAN message types for the Sygnal Hydraulic Power Output (HPO) DBCs. Libraries are named hpo_<dbc>. + Polymath Engineering + Apache-2.0 + + ament_cmake + + dbc_gen_cpp + + + ament_cmake + + diff --git a/sygnal_dbc/io/CMakeLists.txt b/sygnal_dbc/io/CMakeLists.txt new file mode 100644 index 0000000..3b5f8f1 --- /dev/null +++ b/sygnal_dbc/io/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.8) + +project(dbc_sygnal_io VERSION 0.1.0 LANGUAGES C CXX) + +# Auto-generate + export a CAN message library for every *.dbc in this directory. +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) +dbc_package(PREFIX io) diff --git a/sygnal_dbc/database/io/Commissioning.dbc b/sygnal_dbc/io/Commissioning.dbc similarity index 100% rename from sygnal_dbc/database/io/Commissioning.dbc rename to sygnal_dbc/io/Commissioning.dbc diff --git a/sygnal_dbc/database/io/Configuration.dbc b/sygnal_dbc/io/Configuration.dbc similarity index 100% rename from sygnal_dbc/database/io/Configuration.dbc rename to sygnal_dbc/io/Configuration.dbc diff --git a/sygnal_dbc/database/io/Control.dbc b/sygnal_dbc/io/Control.dbc similarity index 100% rename from sygnal_dbc/database/io/Control.dbc rename to sygnal_dbc/io/Control.dbc diff --git a/sygnal_dbc/database/io/Error.dbc b/sygnal_dbc/io/Error.dbc similarity index 100% rename from sygnal_dbc/database/io/Error.dbc rename to sygnal_dbc/io/Error.dbc diff --git a/sygnal_dbc/database/io/Heartbeat.dbc b/sygnal_dbc/io/Heartbeat.dbc similarity index 100% rename from sygnal_dbc/database/io/Heartbeat.dbc rename to sygnal_dbc/io/Heartbeat.dbc diff --git a/sygnal_dbc/database/io/Identify.dbc b/sygnal_dbc/io/Identify.dbc similarity index 100% rename from sygnal_dbc/database/io/Identify.dbc rename to sygnal_dbc/io/Identify.dbc diff --git a/sygnal_dbc/io/package.xml b/sygnal_dbc/io/package.xml new file mode 100644 index 0000000..c43c05b --- /dev/null +++ b/sygnal_dbc/io/package.xml @@ -0,0 +1,17 @@ + + + + dbc_sygnal_io + 0.1.0 + Generated C/C++ CAN message types for the Sygnal IO module DBCs. Libraries are named io_<dbc>. + Polymath Engineering + Apache-2.0 + + ament_cmake + + dbc_gen_cpp + + + ament_cmake + + diff --git a/sygnal_dbc/mcm/CMakeLists.txt b/sygnal_dbc/mcm/CMakeLists.txt new file mode 100644 index 0000000..1910a57 --- /dev/null +++ b/sygnal_dbc/mcm/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.8) + +project(dbc_sygnal_mcm VERSION 0.1.0 LANGUAGES C CXX) + +# Auto-generate + export a CAN message library for every *.dbc in this directory. +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) +dbc_package(PREFIX mcm) diff --git a/sygnal_dbc/database/mcm/Commissioning.dbc b/sygnal_dbc/mcm/Commissioning.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Commissioning.dbc rename to sygnal_dbc/mcm/Commissioning.dbc diff --git a/sygnal_dbc/database/mcm/Configuration.dbc b/sygnal_dbc/mcm/Configuration.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Configuration.dbc rename to sygnal_dbc/mcm/Configuration.dbc diff --git a/sygnal_dbc/database/mcm/Control.dbc b/sygnal_dbc/mcm/Control.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Control.dbc rename to sygnal_dbc/mcm/Control.dbc diff --git a/sygnal_dbc/database/mcm/Error.dbc b/sygnal_dbc/mcm/Error.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Error.dbc rename to sygnal_dbc/mcm/Error.dbc diff --git a/sygnal_dbc/database/mcm/Fault.dbc b/sygnal_dbc/mcm/Fault.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Fault.dbc rename to sygnal_dbc/mcm/Fault.dbc diff --git a/sygnal_dbc/database/mcm/Heartbeat.dbc b/sygnal_dbc/mcm/Heartbeat.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Heartbeat.dbc rename to sygnal_dbc/mcm/Heartbeat.dbc diff --git a/sygnal_dbc/database/mcm/Identify.dbc b/sygnal_dbc/mcm/Identify.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Identify.dbc rename to sygnal_dbc/mcm/Identify.dbc diff --git a/sygnal_dbc/database/mcm/OSCC.dbc b/sygnal_dbc/mcm/OSCC.dbc similarity index 100% rename from sygnal_dbc/database/mcm/OSCC.dbc rename to sygnal_dbc/mcm/OSCC.dbc diff --git a/sygnal_dbc/database/mcm/Override.dbc b/sygnal_dbc/mcm/Override.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Override.dbc rename to sygnal_dbc/mcm/Override.dbc diff --git a/sygnal_dbc/database/mcm/Relay.dbc b/sygnal_dbc/mcm/Relay.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Relay.dbc rename to sygnal_dbc/mcm/Relay.dbc diff --git a/sygnal_dbc/database/mcm/Report.dbc b/sygnal_dbc/mcm/Report.dbc similarity index 100% rename from sygnal_dbc/database/mcm/Report.dbc rename to sygnal_dbc/mcm/Report.dbc diff --git a/sygnal_dbc/mcm/package.xml b/sygnal_dbc/mcm/package.xml new file mode 100644 index 0000000..dfa835b --- /dev/null +++ b/sygnal_dbc/mcm/package.xml @@ -0,0 +1,17 @@ + + + + dbc_sygnal_mcm + 0.1.0 + Generated C/C++ CAN message types for the Sygnal Motor Control Module (MCM) DBCs. Libraries are named mcm_<dbc>. + Polymath Engineering + Apache-2.0 + + ament_cmake + + dbc_gen_cpp + + + ament_cmake + + diff --git a/sygnal_dbc/package.xml b/sygnal_dbc/package.xml deleted file mode 100644 index 8829888..0000000 --- a/sygnal_dbc/package.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - sygnal_dbc - 0.0.0 - DBC database package for Sygnal. Installs DBC files and exposes SYGNAL_DBC_DIR for consumers. - Polymath Engineering - Apache-2.0 - Zeerek Ahmad - - ament_cmake - ament_cmake_auto - python3-cantools-pip - - ament_cmake_pytest - ament_index_python - catch2 - - - ament_cmake - - diff --git a/sygnal_dbc/sygnal_dbc/CMakeLists.txt b/sygnal_dbc/sygnal_dbc/CMakeLists.txt new file mode 100644 index 0000000..14d6d6d --- /dev/null +++ b/sygnal_dbc/sygnal_dbc/CMakeLists.txt @@ -0,0 +1,139 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.5) + +project(sygnal_dbc VERSION 0.1.0 LANGUAGES CXX) + +# Umbrella package: depends on the sibling per-subsystem packages that own the generated +# libraries, and keeps the aggregate view of the raw DBCs (database/ + SYGNAL_DBC_DIR). +find_package(ament_cmake REQUIRED) + +# Re-assemble the database//.dbc layout SYGNAL_DBC_DIR points at. +set(_sygnal_dbc_subsystems cb hpo io mcm vehicles) +foreach(_subsystem IN LISTS _sygnal_dbc_subsystems) + install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../${_subsystem}/ + DESTINATION share/${PROJECT_NAME}/database/${_subsystem} + FILES_MATCHING PATTERN "*.dbc" + ) +endforeach() + +# Its own package (cb_niro_dcan/) since colcon will not descend into cb/, but the +# installed layout keeps it where consumers expect it. +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../cb_niro_dcan/ + DESTINATION share/${PROJECT_NAME}/database/cb/niro_dcan + FILES_MATCHING PATTERN "*.dbc" +) + +# ---- Backwards compatibility ------------------------------------------------- +# This package used to build one aggregate library and install every header as +# sygnal_dbc/_.h. Both are preserved below: an INTERFACE library named +# sygnal_dbc linking every generated library, and forwarding headers. C symbols were +# unchanged by the split (cantools was already given --database-name _), +# so existing consumers need no changes. New code should use /.hpp and link +# dbc_sygnal_:: directly. +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) + +set(_compat_include_dir ${CMAKE_CURRENT_BINARY_DIR}/compat_include) +set(_compat_header_dir ${_compat_include_dir}/${PROJECT_NAME}) +file(MAKE_DIRECTORY ${_compat_header_dir}) + +add_library(${PROJECT_NAME} INTERFACE) + +# Sibling directory name doubles as the library prefix and the package name suffix. +set(_sygnal_dbc_directories cb cb_niro_dcan hpo io mcm vehicles) + +foreach(_directory IN LISTS _sygnal_dbc_directories) + set(_package "dbc_sygnal_${_directory}") + set(_prefix "${_directory}") + + find_package(${_package} REQUIRED) + + file(GLOB _dbc_files CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../${_directory}/*.dbc") + foreach(_dbc ${_dbc_files}) + dbc_library_name(_lib_name "${_prefix}" "${_dbc}") + foreach(_extension h hpp) + file(WRITE "${_compat_header_dir}/${_lib_name}.${_extension}" + "// Generated compatibility shim -- do not edit.\n" + "// Prefer including \"${_lib_name}/${_lib_name}.${_extension}\" directly.\n" + "#pragma once\n" + "#include \"${_lib_name}/${_lib_name}.${_extension}\"\n") + endforeach() + target_link_libraries(${PROJECT_NAME} INTERFACE ${_package}::${_lib_name}) + endforeach() +endforeach() + +target_include_directories(${PROJECT_NAME} INTERFACE + $ + $ +) + +install(DIRECTORY ${_compat_header_dir} DESTINATION include) + +install( + TARGETS ${PROJECT_NAME} + EXPORT export_${PROJECT_NAME} +) +ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET) +ament_export_dependencies( + dbc_sygnal_cb + dbc_sygnal_cb_niro_dcan + dbc_sygnal_hpo + dbc_sygnal_io + dbc_sygnal_mcm + dbc_sygnal_vehicles +) + +if(BUILD_TESTING) + find_package(ament_cmake_pytest REQUIRED) + # Decodes the installed DBC directly, so it also covers the database/ install above. + ament_add_pytest_test(test_mcm_heartbeat_python + test/test_mcm_heartbeat.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + + find_package(Catch2 REQUIRED) + + # Links the sygnal_dbc INTERFACE target and includes sygnal_dbc/mcm_heartbeat.h, i.e. + # exactly what a pre-split consumer does, so the compatibility layer stays covered. + add_executable(test_mcm_heartbeat_cpp test/test_mcm_heartbeat.cpp) + target_link_libraries(test_mcm_heartbeat_cpp PRIVATE Catch2::Catch2WithMain ${PROJECT_NAME}) + + # Covers the C++ API and the four-way Control.dbc name collision that PREFIX prevents. + add_executable(test_generated_cpp_api test/test_generated_cpp_api.cpp) + target_link_libraries(test_generated_cpp_api PRIVATE + Catch2::Catch2WithMain + ${PROJECT_NAME} + dbc_sygnal_mcm::mcm_heartbeat + dbc_sygnal_mcm::mcm_control + dbc_sygnal_io::io_control + dbc_sygnal_cb::cb_control + dbc_sygnal_hpo::hpo_control + ) + + foreach(_test test_mcm_heartbeat_cpp test_generated_cpp_api) + ament_add_test( + ${_test} + GENERATE_RESULT_FOR_RETURN_CODE_ZERO + COMMAND "$" + -r junit -s + -o test_results/${PROJECT_NAME}/${_test}_output.xml + ENV CATCH_CONFIG_CONSOLE_WIDTH=120 + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) + endforeach() +endif() + +ament_package(CONFIG_EXTRAS "sygnal_dbc-extras.cmake") diff --git a/sygnal_dbc/sygnal_dbc/package.xml b/sygnal_dbc/sygnal_dbc/package.xml new file mode 100644 index 0000000..b994dd7 --- /dev/null +++ b/sygnal_dbc/sygnal_dbc/package.xml @@ -0,0 +1,29 @@ + + + sygnal_dbc + 0.1.0 + Umbrella package for the Sygnal DBC databases. Installs the raw DBC files, exposes SYGNAL_DBC_DIR, and depends on the per-subsystem packages that provide the generated C/C++ CAN message libraries. + Polymath Engineering + Apache-2.0 + Zeerek Ahmad + + ament_cmake + + + dbc_sygnal_cb + dbc_sygnal_cb_niro_dcan + dbc_sygnal_hpo + dbc_sygnal_io + dbc_sygnal_mcm + dbc_sygnal_vehicles + + ament_cmake_pytest + ament_index_python + catch2 + python3-cantools-pip + + + ament_cmake + + diff --git a/sygnal_dbc/cmake/sygnal_dbc-extras.cmake b/sygnal_dbc/sygnal_dbc/sygnal_dbc-extras.cmake similarity index 100% rename from sygnal_dbc/cmake/sygnal_dbc-extras.cmake rename to sygnal_dbc/sygnal_dbc/sygnal_dbc-extras.cmake diff --git a/sygnal_dbc/sygnal_dbc/test/test_generated_cpp_api.cpp b/sygnal_dbc/sygnal_dbc/test/test_generated_cpp_api.cpp new file mode 100644 index 0000000..e8adedb --- /dev/null +++ b/sygnal_dbc/sygnal_dbc/test/test_generated_cpp_api.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Covers the C++ API that dbc_gen_cpp adds on top of the cantools C output. The old +// aggregate sygnal_dbc library only ever exposed the C headers, so none of this was +// reachable before the per-subsystem split. + +#if __has_include() + #include +#elif __has_include() + #include +#else + #error "Catch2 headers not found. Please install Catch2 (v2 or v3)." +#endif + +#include + +#include "mcm_heartbeat/mcm_heartbeat.hpp" + +// Same subsystem-agnostic message name in four different DBCs -- see the collision +// test case below. +#include "cb_control/cb_control.hpp" +#include "hpo_control/hpo_control.hpp" +#include "io_control/io_control.hpp" +#include "mcm_control/mcm_control.hpp" + +// The .hpp compatibility shim. Resolving to the same type as the canonical include +// above is the assertion; including both in one translation unit is the test. +#include "sygnal_dbc/mcm_control.hpp" + +namespace +{ +// The frame asserted by test_mcm_heartbeat.cpp / test_mcm_heartbeat.py. +can_frame make_heartbeat_frame() +{ + const uint8_t payload[8] = {0x03, 0x00, 0x00, 0x00, 0x00, 0xCE, 0x10, 0x2D}; + can_frame frame{}; + frame.can_id = mcm_heartbeat::Heartbeat::Id; + frame.can_dlc = mcm_heartbeat::Heartbeat::DataLength; + std::memcpy(frame.data, payload, sizeof(payload)); + return frame; +} +} // namespace + +TEST_CASE("C++ API decodes the same heartbeat frame as the C API") +{ + STATIC_REQUIRE(mcm_heartbeat::Heartbeat::Id == 0x170); + STATIC_REQUIRE(mcm_heartbeat::Heartbeat::DataLength == 8); + STATIC_REQUIRE(mcm_heartbeat::Heartbeat::IsExtendedFrame == false); + + const mcm_heartbeat::Heartbeat msg{make_heartbeat_frame()}; + + REQUIRE(msg.bus_address == 3); + REQUIRE(msg.subsystem_id == 0); + REQUIRE(msg.system_state == 0); + REQUIRE(msg.overall_interface_state == 0); + REQUIRE(msg.interface0_state == 0); + REQUIRE(msg.interface6_state == 0); + REQUIRE(msg.count16 == 0x10CE); + REQUIRE(msg.crc == 0x2D); +} + +TEST_CASE("C++ API rejects a frame whose id belongs to another message") +{ + can_frame frame = make_heartbeat_frame(); + frame.can_id = mcm_heartbeat::Heartbeat::Id + 1; + + REQUIRE_THROWS_AS(mcm_heartbeat::Heartbeat{frame}, std::runtime_error); +} + +TEST_CASE("C++ API round-trips a heartbeat through can_frame") +{ + mcm_heartbeat::Heartbeat original{}; + original.bus_address = 3; + original.subsystem_id = 1; + original.system_state = static_cast(mcm_heartbeat::Heartbeat::SystemState::MCM_CONTROL); + original.count16 = 0x10CE; + original.crc = 0x2D; + + const can_frame frame = static_cast(original); + REQUIRE(frame.can_id == mcm_heartbeat::Heartbeat::Id); + + const mcm_heartbeat::Heartbeat decoded{frame}; + REQUIRE(decoded.bus_address == original.bus_address); + REQUIRE(decoded.subsystem_id == original.subsystem_id); + REQUIRE(decoded.system_state == original.system_state); + REQUIRE(decoded.count16 == original.count16); + REQUIRE(decoded.crc == original.crc); +} + +TEST_CASE("Generated value tables become C++ enums") +{ + using SystemState = mcm_heartbeat::Heartbeat::SystemState; + + STATIC_REQUIRE(static_cast(SystemState::HUMAN_CONTROL) == 0); + STATIC_REQUIRE(static_cast(SystemState::MCM_CONTROL) == 1); + STATIC_REQUIRE(static_cast(SystemState::FAIL_OPERATIONAL_1) == 241); + STATIC_REQUIRE(static_cast(SystemState::FAIL_OPERATIONAL_2) == 242); + STATIC_REQUIRE(static_cast(SystemState::HUMAN_OVERRIDE) == 253); + STATIC_REQUIRE(static_cast(SystemState::FAIL_HARD) == 254); + + const mcm_heartbeat::Heartbeat msg{make_heartbeat_frame()}; + REQUIRE(static_cast(msg.system_state) == SystemState::HUMAN_CONTROL); +} + +// Regression test for the reason dbc_package() requires PREFIX. cb/, hpo/, io/ and mcm/ +// each ship a Control.dbc declaring a ControlCommand at id 352. Without the prefix all +// four would generate a library named "control" defining control_* symbols, and this +// translation unit would fail to compile (redefinition) or fail to link (duplicate +// symbols). Encoding through each one forces the linker to resolve all four packs. +TEST_CASE("Identically-named messages in sibling subsystems stay distinct") +{ + STATIC_REQUIRE(mcm_control::ControlCommand::Id == 352); + STATIC_REQUIRE(io_control::ControlCommand::Id == 352); + STATIC_REQUIRE(cb_control::ControlCommand::Id == 352); + STATIC_REQUIRE(hpo_control::ControlCommand::Id == 352); + + STATIC_REQUIRE_FALSE(std::is_same_v); + STATIC_REQUIRE_FALSE(std::is_same_v); + STATIC_REQUIRE_FALSE(std::is_same_v); + + const can_frame from_mcm = static_cast(mcm_control::ControlCommand{}); + const can_frame from_io = static_cast(io_control::ControlCommand{}); + const can_frame from_cb = static_cast(cb_control::ControlCommand{}); + const can_frame from_hpo = static_cast(hpo_control::ControlCommand{}); + + REQUIRE(from_mcm.can_id == mcm_control::ControlCommand::Id); + REQUIRE(from_io.can_id == io_control::ControlCommand::Id); + REQUIRE(from_cb.can_id == cb_control::ControlCommand::Id); + REQUIRE(from_hpo.can_id == hpo_control::ControlCommand::Id); +} diff --git a/sygnal_dbc/test/test_mcm_heartbeat.cpp b/sygnal_dbc/sygnal_dbc/test/test_mcm_heartbeat.cpp similarity index 94% rename from sygnal_dbc/test/test_mcm_heartbeat.cpp rename to sygnal_dbc/sygnal_dbc/test/test_mcm_heartbeat.cpp index 9d8706b..c9aaf8e 100644 --- a/sygnal_dbc/test/test_mcm_heartbeat.cpp +++ b/sygnal_dbc/sygnal_dbc/test/test_mcm_heartbeat.cpp @@ -24,6 +24,8 @@ extern "C" { +// Deliberately the pre-split include path: this test is what keeps the sygnal_dbc +// compatibility headers honest. #include "sygnal_dbc/mcm_heartbeat.h" } diff --git a/sygnal_dbc/test/test_mcm_heartbeat.py b/sygnal_dbc/sygnal_dbc/test/test_mcm_heartbeat.py similarity index 93% rename from sygnal_dbc/test/test_mcm_heartbeat.py rename to sygnal_dbc/sygnal_dbc/test/test_mcm_heartbeat.py index ad616d7..bb8668c 100644 --- a/sygnal_dbc/test/test_mcm_heartbeat.py +++ b/sygnal_dbc/sygnal_dbc/test/test_mcm_heartbeat.py @@ -35,10 +35,11 @@ def test_decode_mcm_heartbeat_from_installed_dbc(): except Exception: pass - # Fallback to source tree copy (works in non-installed test runs) + # Fallback to source tree copy (works in non-installed test runs). The DBCs live in + # the sibling per-subsystem package directories, one level above this package. if dbc_path is None: - src_root = Path(__file__).resolve().parents[1] - candidate = src_root / 'database/mcm/Heartbeat.dbc' + src_root = Path(__file__).resolve().parents[2] + candidate = src_root / 'mcm/Heartbeat.dbc' if candidate.is_file(): dbc_path = candidate diff --git a/sygnal_dbc/vehicles/CMakeLists.txt b/sygnal_dbc/vehicles/CMakeLists.txt new file mode 100644 index 0000000..696ba54 --- /dev/null +++ b/sygnal_dbc/vehicles/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2025-present Polymath Robotics, Inc. All rights reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.8) + +project(dbc_sygnal_vehicles VERSION 0.1.0 LANGUAGES C CXX) + +# Auto-generate + export a CAN message library for every *.dbc in this directory. +include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/dbc_package.cmake) +dbc_package(PREFIX vehicles) diff --git a/sygnal_dbc/database/vehicles/hyundai_kia_generic.dbc b/sygnal_dbc/vehicles/hyundai_kia_generic.dbc similarity index 100% rename from sygnal_dbc/database/vehicles/hyundai_kia_generic.dbc rename to sygnal_dbc/vehicles/hyundai_kia_generic.dbc diff --git a/sygnal_dbc/vehicles/package.xml b/sygnal_dbc/vehicles/package.xml new file mode 100644 index 0000000..950b374 --- /dev/null +++ b/sygnal_dbc/vehicles/package.xml @@ -0,0 +1,17 @@ + + + + dbc_sygnal_vehicles + 0.1.0 + Generated C/C++ CAN message types for generic vehicle-bus DBCs shipped with Sygnal. Libraries are named vehicles_<dbc>. + Polymath Engineering + Apache-2.0 + + ament_cmake + + dbc_gen_cpp + + + ament_cmake + +