Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ jobs:
echo "tag version $version does not match CMake version $cmake_version" >&2
exit 1
fi
pixi_version="$(sed -nE 's/^version = "([0-9]+\.[0-9]+\.[0-9]+)"$/\1/p' pixi.toml)"
if [[ "$version" != "$pixi_version" ]]; then
echo "tag version $version does not match Pixi version $pixi_version" >&2
exit 1
fi
escaped_version="${version//./\\.}"
if ! grep -Eq "^## ${escaped_version} - [0-9]{4}-[0-9]{2}-[0-9]{2}$" CHANGELOG.md; then
echo "CHANGELOG.md has no dated release heading for $version" >&2
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project are documented in this file.

## 0.2.0 - 2026-07-24

### Added

- Expose the exact signed RDT axis counts on every `Sample` through `raw_wrench`, ordered as force X/Y/Z followed by torque X/Y/Z.

### Changed

- Advance the shared-library ABI for the expanded public `Sample` layout.

## 0.1.3 - 2026-07-23

### Fixed
Expand Down
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)

project(netft VERSION 0.1.3 LANGUAGES CXX)
project(netft VERSION 0.2.0 LANGUAGES CXX)

include(CTest)
include(GNUInstallDirs)
Expand Down Expand Up @@ -42,7 +42,7 @@ target_compile_definitions(netft PRIVATE NETFT_BUILDING_LIBRARY)

set_target_properties(netft PROPERTIES
VERSION "${PROJECT_VERSION}"
SOVERSION 0
SOVERSION 1
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES
)
Expand Down Expand Up @@ -96,7 +96,7 @@ configure_package_config_file(
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/netftConfigVersion.cmake"
VERSION "${PROJECT_VERSION}"
COMPATIBILITY SameMajorVersion
COMPATIBILITY SameMinorVersion
)
install(
EXPORT netftTargets
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ int main() {

netft::Client client{config};
client.start([](const netft::Sample &sample) {
std::cout << sample.force[0] << ' ' << netft::to_string(sample.force_unit) << '\n';
std::cout << sample.raw_wrench[0] << ' ' << sample.force[0] << ' '
<< netft::to_string(sample.force_unit) << '\n';
});
const bool received = client.wait_for_first_sample(std::chrono::seconds{2});
client.stop();
Expand Down Expand Up @@ -107,9 +108,9 @@ failure, and `130` for interruption by `SIGINT`.
Without a manual override, the client requests `http://HOST:HTTP_PORT/netftapi2.xml` before
each streaming session and reads the product name, counts per force unit, counts per torque
unit, force unit, and torque unit. Raw RDT counts are divided by those sensor-selected
calibration scales. Every `Sample` carries the resulting `force_unit`, `torque_unit`, and
configuration revision, and the active configuration is also available through
`Client::health()`.
calibration scales. Every `Sample` carries the exact signed RDT counts in `raw_wrench`, the calibrated `force`
and `torque` values, the resulting `force_unit` and `torque_unit`, and the configuration
revision. The active configuration is also available through `Client::health()`.

Sample values therefore use the units selected by the sensor; the library does not silently
convert them to a preferred unit system. In particular, do not assume that an ATI sensor's
Expand Down
1 change: 1 addition & 0 deletions include/netft/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct Config {

struct Sample {
std::uint32_t rdt_sequence{}, ft_sequence{}, status{};
std::array<std::int32_t, 6> raw_wrench{};
std::array<double, 3> force{}, torque{};
ForceUnit force_unit{ForceUnit::Unknown};
TorqueUnit torque_unit{TorqueUnit::Unknown};
Expand Down
2 changes: 1 addition & 1 deletion pixi.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
name = "netft-cpp"
version = "0.1.3"
version = "0.2.0"
channels = ["conda-forge"]
platforms = ["linux-64", "linux-aarch64"]

Expand Down
1 change: 1 addition & 0 deletions src/detail/client_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ Client::Impl::handle_record(const detail::RawRecord &record,
sample.rdt_sequence = record.rdt_sequence;
sample.ft_sequence = record.ft_sequence;
sample.status = record.status;
sample.raw_wrench = {record.fx, record.fy, record.fz, record.tx, record.ty, record.tz};
sample.force = {record.fx / calibration.counts_per_force_unit,
record.fy / calibration.counts_per_force_unit,
record.fz / calibration.counts_per_force_unit};
Expand Down
2 changes: 1 addition & 1 deletion test/consumer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16)

project(netft_consumer LANGUAGES CXX)

find_package(netft 0.1 CONFIG REQUIRED)
find_package(netft 0.2 CONFIG REQUIRED)

if(DEFINED NETFT_EXPECTED_INCLUDE_DIR)
get_target_property(netft_include_dirs
Expand Down
22 changes: 21 additions & 1 deletion test/install_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ trap cleanup EXIT
package_build="$test_root/package-build"
prefix="$test_root/prefix"
consumer_build="$test_root/consumer-build"
incompatible_consumer_source="$test_root/incompatible-consumer"
incompatible_consumer_build="$test_root/incompatible-consumer-build"
install_include_dir="netft-headers"

cmake_compiler_args=()
Expand Down Expand Up @@ -62,6 +64,24 @@ test -f "$prefix/lib/cmake/netft/netftConfig.cmake"
test -f "$prefix/lib/cmake/netft/netftConfigVersion.cmake"
test -f "$prefix/lib/cmake/netft/netftTargets.cmake"

mkdir -p "$incompatible_consumer_source"
printf '%s\n' \
'cmake_minimum_required(VERSION 3.16)' \
'project(netft_incompatible_consumer LANGUAGES CXX)' \
'if(NOT DEFINED NETFT_PROBE_PREFIX OR NETFT_PROBE_PREFIX STREQUAL "")' \
' message(FATAL_ERROR "NETFT_PROBE_PREFIX must be defined and nonempty")' \
'endif()' \
'find_package(netft 0.1 CONFIG QUIET' \
' PATHS "${NETFT_PROBE_PREFIX}" NO_DEFAULT_PATH)' \
'if(netft_FOUND)' \
' message(FATAL_ERROR "netft 0.2 package accepted incompatible 0.1 request")' \
'endif()' \
>"$incompatible_consumer_source/CMakeLists.txt"
cmake -S "$incompatible_consumer_source" \
-B "$incompatible_consumer_build" -G Ninja \
"${cmake_compiler_args[@]}" \
-DNETFT_PROBE_PREFIX="$prefix"

if find "$prefix" -name '*netft_cli_lib*' -print -quit | grep -q .; then
echo "private netft_cli_lib was installed" >&2
exit 1
Expand Down Expand Up @@ -150,7 +170,7 @@ if [[ "$mode" == "shared" ]]; then
LD_LIBRARY_PATH="$prefix/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
ldd "$prefix/bin/netft"
)"
grep -Fq "$prefix/lib/libnetft.so.0" <<<"$cli_dependencies"
grep -Fq "$prefix/lib/libnetft.so.1" <<<"$cli_dependencies"
if grep -Fq 'netft_cli_lib' <<<"$cli_dependencies"; then
echo "$cli_dependencies" >&2
exit 1
Expand Down
8 changes: 6 additions & 2 deletions test/test_client_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ TEST(ClientStream, ConvertsNativeUnitsAndPublishesLatestSample) {
netft::test::FakeSensor sensor;
sensor.pause();
sensor.queue_record(1, 0, 100,
{1'000'000, -2'000'000, 3'000'000, 1'000'000, -2'000'000, 3'000'000});
{1'000'000, -2'000'000, 3'000'000, 4'000'000, -5'000'000, 6'000'000});
const std::array<std::int32_t, 6> expected_raw{1'000'000, -2'000'000, 3'000'000,
4'000'000, -5'000'000, 6'000'000};
netft::Client client{config_for(sensor)};
std::mutex callback_mutex;
std::optional<netft::Sample> callback_sample;
Expand All @@ -115,15 +117,17 @@ TEST(ClientStream, ConvertsNativeUnitsAndPublishesLatestSample) {
ASSERT_TRUE(sample);
EXPECT_EQ(sample->rdt_sequence, 1U);
EXPECT_EQ(sample->ft_sequence, 100U);
EXPECT_EQ(sample->raw_wrench, expected_raw);
EXPECT_EQ(sample->force, (std::array<double, 3>{1.0, -2.0, 3.0}));
EXPECT_EQ(sample->torque, (std::array<double, 3>{1.0, -2.0, 3.0}));
EXPECT_EQ(sample->torque, (std::array<double, 3>{4.0, -5.0, 6.0}));
EXPECT_EQ(sample->force_unit, netft::ForceUnit::Newton);
EXPECT_EQ(sample->torque_unit, netft::TorqueUnit::NewtonMeter);
EXPECT_EQ(sample->configuration_revision, 1U);
{
std::lock_guard<std::mutex> lock(callback_mutex);
ASSERT_TRUE(callback_sample);
EXPECT_EQ(callback_sample->rdt_sequence, sample->rdt_sequence);
EXPECT_EQ(callback_sample->raw_wrench, expected_raw);
EXPECT_EQ(callback_sample->force, sample->force);
EXPECT_EQ(callback_sample->torque, sample->torque);
}
Expand Down
Loading