Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8a3ab11
Add snapshot core build contract
han-xudong Jul 22, 2026
b0460e3
core: snapshot netft-cpp v0.1.2
han-xudong Jul 22, 2026
dcce801
feat: convert sensor-native samples to SI units
han-xudong Jul 22, 2026
cbcafb8
feat: use sensor calibration by default
han-xudong Jul 22, 2026
69de355
refactor: adapt ROS diagnostics to netft core health
han-xudong Jul 23, 2026
369e4b7
refactor: run netft_check on the snapshot core
han-xudong Jul 23, 2026
c95f546
refactor: run ROS nodes on the snapshot core
han-xudong Jul 23, 2026
e8818a6
refactor: run ros2_control on the snapshot core
han-xudong Jul 23, 2026
21265a4
fix: address ros2_control migration review
han-xudong Jul 23, 2026
391282f
feat: configure automatic sensor calibration in ROS
han-xudong Jul 23, 2026
fd1b8de
fix: default ros2 control sensor endpoint
han-xudong Jul 23, 2026
c46ddb7
refactor: remove the original Net F/T runtime
han-xudong Jul 23, 2026
11f6061
docs: explain core provenance and calibration behavior
han-xudong Jul 23, 2026
227ee20
release: prepare netft_driver 0.3.0
han-xudong Jul 23, 2026
f4cabc4
fix: address final core migration review
han-xudong Jul 23, 2026
4fff144
test: support source trees without git metadata
han-xudong Jul 23, 2026
4f66f52
chore: remove internal verification report
han-xudong Jul 23, 2026
4db99ff
fix: support libstdc++ without float from_chars
han-xudong Jul 23, 2026
629e554
fix: hide ros2 control implementation symbols
han-xudong Jul 23, 2026
a1a8622
test: wait for asynchronous stop command
han-xudong Jul 23, 2026
3a60dc6
docs: clarify compatibility and license boundaries
han-xudong Jul 23, 2026
f7eeb31
test: support Noetic Python 3.8
han-xudong Jul 23, 2026
ee99352
test: keep healthy sensor sample observable
han-xudong Jul 23, 2026
be78bab
license: adopt Apache-2.0 project-wide
han-xudong Jul 23, 2026
3377f6e
fix: address final review feedback
han-xudong Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ jobs:
with:
persist-credentials: false
- name: Install coverage dependencies
run: sudo apt-get update && sudo apt-get install -y gcovr libgtest-dev
run: >-
sudo apt-get update && sudo apt-get install -y
gcovr libcurl4-openssl-dev libgtest-dev
- name: Configure coverage build
run: >-
cmake -S . -B build/coverage
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
Changelog for package netft_driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0.3.0 (2026-07-22)
------------------
* Add a private ``netft-cpp`` v0.1.2 core snapshot and remove the duplicate original runtime.
* Read sensor-reported calibration by default and convert its native force and torque units to N and Nm.
* Add the explicit ``use_sensor_calibration=false`` manual override, with counts/N and counts/Nm inputs.
* Add HTTP configuration port and timeout parameters for sensor calibration discovery.
* Support Noetic's GCC 9 standard library with an Apache-2.0 XML parser compatibility translation unit.
* Keep the ros2_control plugin ABI private by hiding core and implementation symbols.
* License the complete repository under Apache-2.0, matching the upstream core.

0.2.2 (2026-07-22)
------------------
* Replace the project-specific sensor address in defaults and examples with
Expand Down
191 changes: 157 additions & 34 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.16)
project(netft_driver)

set(CMAKE_CXX_STANDARD 17)
Expand All @@ -8,20 +8,74 @@ set(CMAKE_CXX_EXTENSIONS OFF)
option(NETFT_CORE_ONLY "Build only the ROS-neutral C++ core" OFF)
option(NETFT_INSTALL_TEST_TOOLS "Install private integration-test tools" OFF)

add_library(netft_core STATIC src/protocol.cpp src/status.cpp src/client.cpp)
find_package(Threads REQUIRED)
find_package(CURL 7.63 REQUIRED)

include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
[=[
#include <charconv>
#include <system_error>

int main() {
const char input[] = "1";
double value{};
const auto result =
std::from_chars(input, input + 1, value, std::chars_format::general);
return result.ec == std::errc{} ? 0 : 1;
}
]=]
NETFT_HAS_FLOAT_FROM_CHARS)

set(NETFT_SNAPSHOT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/core")
if(NETFT_HAS_FLOAT_FROM_CHARS)
set(NETFT_XML_CONFIG_SOURCE
"${NETFT_SNAPSHOT_SOURCE_DIR}/src/detail/xml_config.cpp")
else()
set(NETFT_XML_CONFIG_SOURCE
"${CMAKE_CURRENT_SOURCE_DIR}/src/compat/xml_config.cpp")
endif()
set(NETFT_SNAPSHOT_SOURCES
src/core/src/types.cpp
src/core/src/status.cpp
src/core/src/discovery.cpp
src/core/src/client.cpp
src/core/src/detail/client_impl.cpp
src/core/src/detail/fault_latch.cpp
src/core/src/detail/posix_transport.cpp
src/core/src/detail/protocol.cpp
src/core/src/detail/sequence.cpp
${NETFT_XML_CONFIG_SOURCE}
)

add_library(netft_core STATIC ${NETFT_SNAPSHOT_SOURCES})
set_target_properties(netft_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_features(netft_core PUBLIC cxx_std_17)
target_include_directories(netft_core PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
target_compile_definitions(netft_core PRIVATE NETFT_BUILDING_LIBRARY)
target_include_directories(netft_core
PUBLIC ${NETFT_SNAPSHOT_SOURCE_DIR}/include
PRIVATE ${NETFT_SNAPSHOT_SOURCE_DIR}/src
)
target_link_libraries(netft_core PUBLIC Threads::Threads CURL::libcurl)

add_library(netft_ros_support STATIC
src/ros/unit_conversion.cpp
src/ros/diagnostics.cpp)
set_target_properties(netft_ros_support PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_features(netft_ros_support PUBLIC cxx_std_17)
target_include_directories(netft_ros_support
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${NETFT_SNAPSHOT_SOURCE_DIR}/include)
target_link_libraries(netft_ros_support PUBLIC netft_core)

add_executable(netft_check src/check_main.cpp)
find_package(Threads REQUIRED)
target_link_libraries(netft_check PRIVATE netft_core Threads::Threads)
target_link_libraries(netft_check PRIVATE netft_ros_support netft_core)

if(NETFT_CORE_ONLY)
enable_testing()
find_package(GTest REQUIRED)
add_subdirectory(test/core)
add_subdirectory(test/cpp)
return()
endif()
Expand All @@ -46,12 +100,13 @@ endif()
if(NETFT_ROS_VERSION STREQUAL "1")
find_package(catkin REQUIRED COMPONENTS roscpp geometry_msgs std_srvs diagnostic_msgs)
catkin_package(
INCLUDE_DIRS include
LIBRARIES netft_core
CATKIN_DEPENDS roscpp geometry_msgs std_srvs diagnostic_msgs)
add_executable(netft_node_cpp src/ros1_node.cpp)
set_target_properties(netft_node_cpp PROPERTIES OUTPUT_NAME netft_node)
target_link_libraries(netft_node_cpp PRIVATE netft_core ${catkin_LIBRARIES})
target_link_libraries(netft_node_cpp PRIVATE
netft_ros_support
netft_core
${catkin_LIBRARIES})
target_include_directories(netft_node_cpp PRIVATE ${catkin_INCLUDE_DIRS})
if(CATKIN_ENABLE_TESTING)
catkin_run_tests_target(
Expand All @@ -73,15 +128,15 @@ if(NETFT_ROS_VERSION STREQUAL "1")
FILES config/netft_ros1.yaml
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config
)
install(TARGETS netft_core ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION})
install(TARGETS netft_node_cpp netft_check
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION})
if(CATKIN_ENABLE_TESTING)
catkin_add_gtest(netft_ros1_node test/cpp/test_ros1_node.cpp)
if(TARGET netft_ros1_node)
target_link_libraries(netft_ros1_node netft_core ${catkin_LIBRARIES})
target_link_libraries(netft_ros1_node
netft_ros_support
netft_core
${catkin_LIBRARIES})
target_include_directories(netft_ros1_node PRIVATE ${catkin_INCLUDE_DIRS})
endif()
endif()
Expand All @@ -99,25 +154,42 @@ elseif(NETFT_ROS_VERSION STREQUAL "2")
else()
set(NETFT_ROS2_CONTROL_API_DEFINITION NETFT_ROS2_CONTROL_MODERN_API)
endif()

function(netft_configure_ros2_control_target target)
set_target_properties(${target} PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES)
target_compile_definitions(${target} PRIVATE
${NETFT_ROS2_CONTROL_API_DEFINITION})
target_include_directories(${target} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(${target} PRIVATE
netft_ros_support
netft_core
${hardware_interface_TARGETS}
${pluginlib_TARGETS}
${rclcpp_TARGETS}
${realtime_tools_TARGETS}
${std_srvs_TARGETS}
${diagnostic_msgs_TARGETS}
)
if(
CMAKE_SYSTEM_NAME STREQUAL "Linux"
AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$"
)
target_link_options(${target} PRIVATE
"LINKER:--exclude-libs,libnetft_core.a:libnetft_ros_support.a")
endif()
endfunction()

add_library(netft_ros2_control SHARED src/netft_hardware_interface.cpp)
target_compile_definitions(netft_ros2_control PRIVATE
${NETFT_ROS2_CONTROL_API_DEFINITION})
target_include_directories(netft_ros2_control PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_link_libraries(netft_ros2_control PRIVATE
netft_core
${hardware_interface_TARGETS}
${pluginlib_TARGETS}
${rclcpp_TARGETS}
${realtime_tools_TARGETS}
${std_srvs_TARGETS}
${diagnostic_msgs_TARGETS}
)
netft_configure_ros2_control_target(netft_ros2_control)
pluginlib_export_plugin_description_file(
hardware_interface netft_hardware_plugins.xml)
add_executable(netft_node_cpp src/ros2_node.cpp)
set_target_properties(netft_node_cpp PROPERTIES OUTPUT_NAME netft_node)
target_link_libraries(netft_node_cpp PRIVATE
netft_ros_support
netft_core
${rclcpp_TARGETS}
${geometry_msgs_TARGETS}
Expand Down Expand Up @@ -163,13 +235,49 @@ elseif(NETFT_ROS_VERSION STREQUAL "2")
)
endif()
if(BUILD_TESTING)
target_compile_definitions(netft_ros2_control PRIVATE
NETFT_ROS2_CONTROL_TESTING)
find_package(ament_cmake_pytest REQUIRED)
find_package(ament_cmake_gtest REQUIRED)

set(
NETFT_ROS2_CONTROL_TEST_PREFIX
"${CMAKE_CURRENT_BINARY_DIR}/netft_ros2_control_test_prefix")
set(
NETFT_ROS2_CONTROL_TEST_SHARE
"${NETFT_ROS2_CONTROL_TEST_PREFIX}/share/${PROJECT_NAME}")
file(MAKE_DIRECTORY
"${NETFT_ROS2_CONTROL_TEST_PREFIX}/lib"
"${NETFT_ROS2_CONTROL_TEST_PREFIX}/share/ament_index/resource_index/packages"
"${NETFT_ROS2_CONTROL_TEST_PREFIX}/share/ament_index/resource_index/hardware_interface__pluginlib__plugin"
"${NETFT_ROS2_CONTROL_TEST_SHARE}")
configure_file(
test/cpp/netft_hardware_test_plugins.xml
"${NETFT_ROS2_CONTROL_TEST_SHARE}/netft_hardware_test_plugins.xml"
COPYONLY)
configure_file(
package.xml
"${NETFT_ROS2_CONTROL_TEST_SHARE}/package.xml"
COPYONLY)
file(WRITE
"${NETFT_ROS2_CONTROL_TEST_PREFIX}/share/ament_index/resource_index/packages/${PROJECT_NAME}"
"")
file(WRITE
"${NETFT_ROS2_CONTROL_TEST_PREFIX}/share/ament_index/resource_index/hardware_interface__pluginlib__plugin/${PROJECT_NAME}"
"share/${PROJECT_NAME}/netft_hardware_test_plugins.xml")

add_library(
netft_ros2_control_testing SHARED
src/netft_hardware_interface.cpp)
netft_configure_ros2_control_target(netft_ros2_control_testing)
target_compile_definitions(netft_ros2_control_testing PRIVATE
NETFT_ROS2_CONTROL_TESTING)
set_target_properties(netft_ros2_control_testing PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${NETFT_ROS2_CONTROL_TEST_PREFIX}/lib"
RUNTIME_OUTPUT_DIRECTORY "${NETFT_ROS2_CONTROL_TEST_PREFIX}/lib")

ament_add_gtest(netft_ros2_node test/cpp/test_ros2_node.cpp)
if(TARGET netft_ros2_node)
target_link_libraries(netft_ros2_node
netft_ros_support
netft_core
${rclcpp_TARGETS}
${geometry_msgs_TARGETS}
Expand All @@ -179,26 +287,43 @@ elseif(NETFT_ROS_VERSION STREQUAL "2")
endif()
ament_add_gtest(netft_hardware_interface
test/cpp/test_hardware_interface.cpp
test/cpp/support/fake_sensor.cpp
test/cpp/support/ros2_control_test_access.cpp
test/core/support/fake_http_server.cpp
test/core/support/fake_sensor.cpp
ENV
"AMENT_PREFIX_PATH=${NETFT_ROS2_CONTROL_TEST_PREFIX}:$ENV{AMENT_PREFIX_PATH}"
)
if(TARGET netft_hardware_interface)
target_compile_definitions(netft_hardware_interface PRIVATE
${NETFT_ROS2_CONTROL_API_DEFINITION}
NETFT_ROS2_CONTROL_TESTING)
${NETFT_ROS2_CONTROL_API_DEFINITION})
target_include_directories(netft_hardware_interface PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/test/core
${NETFT_SNAPSHOT_SOURCE_DIR}/src)
target_link_libraries(netft_hardware_interface
netft_ros2_control
netft_ros_support
netft_core
${CMAKE_DL_LIBS}
${hardware_interface_TARGETS}
${rclcpp_TARGETS}
${std_srvs_TARGETS}
${diagnostic_msgs_TARGETS}
)
add_dependencies(
netft_hardware_interface
netft_ros2_control_testing)
endif()
ament_add_pytest_test(netft_unit test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
APPEND_ENV PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}
TIMEOUT 90
)
add_test(
NAME netft_ros2_control_symbol_audit
COMMAND
"${CMAKE_CURRENT_SOURCE_DIR}/test/integration/audit_ros2_control_symbols.sh"
"$<TARGET_FILE:netft_ros2_control>"
)
ament_add_pytest_test(netft_ros2_smoke_harness test/test_shell_harness.py
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
APPEND_ENV PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}
Expand All @@ -218,9 +343,7 @@ elseif(NETFT_ROS_VERSION STREQUAL "2")
TIMEOUT 30
)
endif()
install(TARGETS netft_core ARCHIVE DESTINATION lib)
install(TARGETS netft_node_cpp netft_check DESTINATION lib/${PROJECT_NAME})
install(DIRECTORY include/${PROJECT_NAME}/ DESTINATION include/${PROJECT_NAME})
install(TARGETS netft_ros2_control
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
Expand Down
19 changes: 17 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ Before opening a pull request:
Open an issue before starting a large feature or an interface-breaking change
so the scope can be agreed upon first.

## Core snapshot updates

1. Update and release `netft-cpp` first.
2. Copy only the library paths from an immutable upstream tag.
3. Preserve the `netft` namespace and the Apache-2.0 license.
4. Update `src/core/UPSTREAM` with the copied tag and commit.
5. Keep the Apache-2.0 `src/compat/xml_config.cpp` fallback synchronized with the copied upstream XML parser.
6. Rerun the byte comparison and every snapshot and ROS test.
7. Keep ROS-specific changes out of `src/core/`; port reusable fixes upstream before copying them back.

Do not add an automated downloader or submodule for the core snapshot.

## Physical sensor safety

The automated test suite does not require a physical sensor. Do not perform
Expand All @@ -80,5 +92,8 @@ issued. Do not publish private network details or sensor identifiers.

## License

By contributing, you agree that your contribution is licensed under the
repository's [MIT License](LICENSE).
Contributions are licensed under the repository's
[Apache-2.0 License](LICENSE). The copied `src/core/` snapshot retains the
upstream license copy at [src/core/LICENSE](src/core/LICENSE). Propose reusable
core fixes upstream first, then copy them from a released immutable upstream
revision and update the fallback when the XML parser changes.
Loading