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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Preserve the byte inventory when Windows enables core.autocrlf.
/core/** -text
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
with:
cache: true
- name: Verify controlled core snapshot
Expand Down Expand Up @@ -85,7 +85,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
with:
cache: true
- name: Configure
Expand Down Expand Up @@ -113,7 +113,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
with:
cache: true
- name: Configure
Expand Down Expand Up @@ -145,7 +145,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
with:
cache: true
- name: Verify pinned CMake version
Expand All @@ -169,7 +169,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
with:
cache: true
- name: Configure
Expand Down Expand Up @@ -205,7 +205,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
with:
cache: true
- name: Configure
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,23 @@ jobs:
persist-credentials: false
- name: Initialize Python CodeQL
if: matrix.language == 'python'
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
- name: Initialize C++ CodeQL with inline suppressions
if: matrix.language == 'c-cpp'
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
packs: ${{ matrix.suppression-pack }}
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
if: matrix.language == 'c-cpp'
with:
cache: true
- name: Build extension for C++ analysis
if: matrix.language == 'c-cpp'
run: pixi run install
- name: Analyze
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4
6 changes: 3 additions & 3 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: prefix-dev/setup-pixi@v0.10.0
- uses: prefix-dev/setup-pixi@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
with:
cache: true
- name: Generate Python and native coverage
run: pixi run coverage
- name: Upload Python coverage
uses: codecov/codecov-action@v7
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
continue-on-error: true
with:
files: coverage.xml
disable_search: true
flags: python
use_oidc: true
- name: Upload native coverage
uses: codecov/codecov-action@v7
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
continue-on-error: true
with:
files: coverage-native.xml
Expand Down
26 changes: 22 additions & 4 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ on:
push:
branches: [main]
workflow_dispatch:
inputs:
windows_only:
description: Build only the Windows CPython 3.10 smoke wheel
type: boolean
default: false

permissions:
contents: read
Expand All @@ -15,6 +20,7 @@ concurrency:

jobs:
curl-build:
if: ${{ !inputs.windows_only }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
Expand Down Expand Up @@ -46,6 +52,8 @@ jobs:
with:
python-version: "3.14"
cache: pip
- name: Verify controlled core snapshot
run: python tools/sync_core.py verify
- name: Install wheel build frontend
run: python -m pip install cibuildwheel==3.4.1 auditwheel
- name: Build CPython 3.10 x86_64 wheel
Expand Down Expand Up @@ -85,6 +93,8 @@ jobs:
with:
python-version: "3.14"
cache: pip
- name: Verify controlled core snapshot
run: python tools/sync_core.py verify
- name: Install wheel build frontend
run: python -m pip install cibuildwheel==3.4.1 delocate
- name: Build CPython 3.10 native macOS wheel
Expand All @@ -105,7 +115,7 @@ jobs:
if-no-files-found: error

windows-smoke:
if: github.event_name == 'pull_request'
if: ${{ always() && !cancelled() && ((github.event_name == 'pull_request' && needs.curl-build.result == 'success') || inputs.windows_only) }}
needs: curl-build
runs-on: windows-2025
steps:
Expand All @@ -116,6 +126,8 @@ jobs:
with:
python-version: "3.14"
cache: pip
- name: Verify controlled core snapshot
run: python tools/sync_core.py verify
- name: Install wheel build frontend
run: python -m pip install cibuildwheel==3.4.1 pefile
- name: Build CPython 3.10 Windows wheel
Expand All @@ -135,7 +147,7 @@ jobs:
if-no-files-found: error

wheels:
if: github.event_name != 'pull_request'
if: github.event_name != 'pull_request' && !inputs.windows_only
needs: curl-build
strategy:
fail-fast: false
Expand All @@ -154,6 +166,8 @@ jobs:
with:
python-version: "3.14"
cache: pip
- name: Verify controlled core snapshot
run: python tools/sync_core.py verify
- name: Install wheel build frontend
run: python -m pip install cibuildwheel==3.4.1 auditwheel
- name: Build supported wheels
Expand All @@ -173,7 +187,7 @@ jobs:
if-no-files-found: error

macos-wheels:
if: github.event_name != 'pull_request'
if: github.event_name != 'pull_request' && !inputs.windows_only
needs: curl-build
strategy:
fail-fast: false
Expand All @@ -192,6 +206,8 @@ jobs:
with:
python-version: "3.14"
cache: pip
- name: Verify controlled core snapshot
run: python tools/sync_core.py verify
- name: Install wheel build frontend
run: python -m pip install cibuildwheel==3.4.1 delocate
- name: Build supported native macOS wheels
Expand All @@ -211,7 +227,7 @@ jobs:
if-no-files-found: error

windows-wheels:
if: github.event_name != 'pull_request'
if: github.event_name != 'pull_request' && !inputs.windows_only
needs: curl-build
runs-on: windows-2025
steps:
Expand All @@ -222,6 +238,8 @@ jobs:
with:
python-version: "3.14"
cache: pip
- name: Verify controlled core snapshot
run: python tools/sync_core.py verify
- name: Install wheel build frontend
run: python -m pip install cibuildwheel==3.4.1 pefile
- name: Build supported Windows wheels
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project are documented in this file.

## Unreleased

## 2.1.2 - 2026-10-08

- Integrate netft-cpp 0.3.4's calibration and checked-time protections.
- Support exact-commit snapshot synchronization and pin reviewed build actions.
- Explain latest-value versus bounded capture queues, drop counters, legacy migration and seven-repository maintenance.

## 2.1.1 - 2026-08-14

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(pynetft VERSION 2.1.1 LANGUAGES CXX)
project(pynetft VERSION 2.1.2 LANGUAGES CXX)

option(PYNETFT_BUILD_TESTING "Build pyNetFT native tests" OFF)

Expand Down
21 changes: 18 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,17 @@ Pass the authorized address only through `NETFT_SENSOR_HOST`. Never commit it. T

## Cross-repository synchronization

The `netft` organization maintains three separate repositories:
The `netft` organization maintains seven separate repositories:

- `netft-cpp` is the standalone C++ core and source of truth for shared protocol behavior;
- `pyNetFT` contains a pinned private core snapshot, Python bindings, and the Python public API; and
- `ros-netft` contains its own private core snapshot and ROS integration.
- `ros-netft` contains its own private core snapshot and ROS integration;
- `netft-cli` owns terminal workflows and its reviewed core adaptation;
- `netft-viewer` owns desktop interaction, companion and packaging;
- `netft-docs` maintains shared user guides and version-pinned references at https://netft.dev; and
- `.github` maintains organization navigation, common templates and issue routing.

There is no automatic runtime or build-time dependency between these repositories. Synchronize a released core into each consumer through separate, reviewable pull requests. Record the exact upstream tag and commit, port consumer-specific integration independently, run that repository's full supported tests, and document intentional differences. Do not claim the snapshots update in lockstep.
There is no automatic runtime or build-time dependency between these repositories. Synchronize a released core into each consumer through separate, reviewable pull requests. Record the exact upstream tag and commit, port consumer-specific integration independently, run the necessary checks for the affected contract and supported release platforms, and document intentional differences. Do not claim the snapshots update in lockstep.

## Release workflow

Expand Down Expand Up @@ -124,3 +128,14 @@ Maintainers must verify the publisher owner, repository, workflow filename, and
Keep each pull request limited to one coherent change. Describe the problem, chosen behavior, tests run, supported platforms, hardware involvement, and any core or ROS synchronization impact. Update public documentation and `CHANGELOG.md` when users need to know about a change.

All contributions are submitted under the [Apache License 2.0](https://github.com/netft/pyNetFT/blob/main/LICENSE). The prior pyNetFT MIT text and third-party license notices remain in `LICENSES/`.

### Current core candidate

The private snapshot uses the exact netft-cpp `v0.3.4` release commit `1ce68a08a9387d9a36d22c3c259e44533199c347`; `UPSTREAM` and its content manifest record that release identity. Update from a clean upstream checkout using:

```sh
python tools/sync_core.py sync --source /path/to/netft-cpp --tag v0.3.4
python tools/sync_core.py verify
```

The manifest checks the selected bytes, including provenance. It detects drift and does not authenticate a remote release. Consumer build files remain private; fixes to core source belong upstream.
2 changes: 1 addition & 1 deletion core/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.3.3 LANGUAGES CXX)
project(netft VERSION 0.3.4 LANGUAGES CXX)

include(CTest)
include(GNUInstallDirs)
Expand Down
15 changes: 8 additions & 7 deletions core/SNAPSHOT.sha256
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
3db6fdfbb62d6073fa3f77784235cfe2eff06a1f102fb1d0f3a3f6da02dafa01 CMakeLists.txt
b2329e8fa84798edb7ddf912d6ea79455bd8181d607b28ae94297e2f409398f4 CMakeLists.txt
c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 LICENSE
926aedecaf33e0d9a178360ea455ee9cbdd54537f89d75835409fdb28fdcd908 UPSTREAM
ce471ce02972730df08fb35bc3c3ea60346dc4e79f7780ad1d55126d1cb61395 UPSTREAM
40701f04e67bb18052b6c8f1db60cf5d41baab09e5396f5a88c1eb14ad1a7139 cmake/netftConfig.cmake.in
e4a52fe7c7e13de95a29bdbd688d295a4a8ddc1805276274b0c14d4092f7253d include/netft/client.hpp
4f7fb383da5456fd8477e1981f4ff3eba9b82ec3a98f3c395e7d482faf75c112 include/netft/discovery.hpp
1beed1a22ad16aa801e0cceb0950548a32d263e01f260a86e72ec58d8f8d1a74 include/netft/export.hpp
912892bcb8bb5f95392c9c74983d91aab103b7adc5735505b0ac97e1012893b1 include/netft/status.hpp
a7c899858aa7a0a28780504f14c1ecdaebb375607119a03500903f3782f45a68 include/netft/types.hpp
7199f02fa0fd88059323f507edbf073b2a4827c692d1fd824893cbad8ea9215b src/client.cpp
a7e0bcba7a8cd5ccb1ab04658ea735f2b6a3f2bac81c3bce95578e3c14349650 src/detail/client_impl.cpp
d4a7c89afa6a98ef687e7ecb266c8ce3287ec2f01c4f14d29278cf3cce140640 src/detail/client_impl.cpp
b202cc6e250866e681d16b480911f77d865d9187300111f8482bc4a8b0424b34 src/detail/client_impl.hpp
ad8744385642693bb4666c428a5dbd0d6eff350b0079a6c9ff720512e89b6a53 src/detail/fault_latch.cpp
be488c557e17044948cd62e6de07bf54862d389a584b40b78336479cecdaf23c src/detail/fault_latch.hpp
f5953a7fc4fd5eee56f7aa3568fe7417010e3e767cfbb0ee5922ee5f8aa2a9aa src/detail/protocol.cpp
8ef13600d67b2bb77181c5403e9b51b7f48b6f8ef06381fb1f07c5b9b2a90d8a src/detail/protocol.hpp
fe9034a4a32138c3469005a44a1fbd61cfe41df25207723635a22e8ed376dbf5 src/detail/sequence.cpp
72daab01251425dd7fd5d26334760f4937f94e674a16df0fb38301b0ee78b9c1 src/detail/sequence.hpp
8fdc8f3373008f41ea032478ac44ef5274013eeb4ea56f5fdd265895672ee0ae src/detail/time.hpp
78098e0c98c84103653b7aa9e2baf689cde656b52e1e13873f7491aed7f35ff5 src/detail/udp_transport.hpp
6c4fb6a8b6bdfc9f1364ac0d76423e9972e1eb30bb987aa6865e191e7d93f9ea src/detail/udp_transport_posix.cpp
9a9e34da6e9d66e0918e85da61ebc60f9ae3806a0471c327f818e8ccd210426e src/detail/udp_transport_windows.cpp
3e908eac916b3cf457387fd878dd77fb46e3b5a9eee37dfc60be788649476475 src/detail/xml_config.cpp
f6e7e14de70a366e3d25b139dee8f061cdfff4c5173c78b831e564125b81d4b2 src/detail/udp_transport_posix.cpp
d48e67880a9c0db5f2c9b4045cc7caddb7fdfbb934cb676d25914c9cf2d5cfe5 src/detail/udp_transport_windows.cpp
95a05fe378b735b433ea10f438e9f5338120830e054b30ed11cd8db12d760cea src/detail/xml_config.cpp
f10ff61cd2cc645abca23a7abce37439273569c6fe139a4fb07041532edb908d src/detail/xml_config.hpp
5a25276d0307c6a341aaee525cc9a66f00cda06e30bd345716ff5ee17a57e3e7 src/discovery.cpp
2598c182a2322925c2d99e1d0bba3b57215e451ba1ecc8df6b108b885f089c1f src/status.cpp
6017859e709c6b80fd5dc444554e497410e1c7d19a12ae05502f0c9922c58762 src/types.cpp
2b59ff7f0c88207e47d295168eaf4383fbecc008b5e4f1346fb63fddfe02b72b src/types.cpp
4 changes: 2 additions & 2 deletions core/UPSTREAM
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
repository=https://github.com/netft/netft-cpp
tag=v0.3.3
commit=3259b8576b16fb150b51e9ef9090366c5e6efcba
tag=v0.3.4
commit=1ce68a08a9387d9a36d22c3c259e44533199c347
paths=CMakeLists.txt,LICENSE,cmake,include,src
18 changes: 14 additions & 4 deletions core/src/detail/client_impl.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "detail/client_impl.hpp"
#include "detail/time.hpp"

#include <algorithm>
#include <array>
#include <cmath>
#include <exception>
#include <stdexcept>
#include <utility>
Expand Down Expand Up @@ -301,12 +303,13 @@ void Client::Impl::bias() {
}

bool Client::Impl::wait_for_first_sample(const std::chrono::duration<double> timeout) {
const auto deadline = detail::checked_deadline(std::chrono::steady_clock::now(), timeout);
std::unique_lock<std::mutex> data_lock(data_mutex_);
const auto captured_generation = generation_;
if (captured_generation == 0) {
return false;
}
first_sample_cv_.wait_for(data_lock, timeout, [this, captured_generation] {
first_sample_cv_.wait_until(data_lock, deadline, [this, captured_generation] {
return generation_ != captured_generation || delivered_generation_ == captured_generation ||
stopping_ || faulted();
});
Expand Down Expand Up @@ -406,7 +409,9 @@ void Client::Impl::run() noexcept {
}

std::unique_lock<std::mutex> data_lock(data_mutex_);
first_sample_cv_.wait_for(data_lock, backoff, [this] { return stopping_.load(); });
first_sample_cv_.wait_until(data_lock,
detail::checked_deadline(detail::SteadyClock::now(), backoff),
[this] { return stopping_.load(); });
data_lock.unlock();
backoff = std::min(backoff * 2.0, config_.reconnect_max_delay);
}
Expand Down Expand Up @@ -461,7 +466,7 @@ Client::Impl::SessionOutcome Client::Impl::receive_session() {
}

const auto timeout = config_.receive_timeout;
auto deadline = std::chrono::steady_clock::now() + timeout;
auto deadline = detail::checked_deadline(std::chrono::steady_clock::now(), timeout);
std::array<std::uint8_t, 65'536> buffer{};
while (!stopping_) {
const auto now = std::chrono::steady_clock::now();
Expand Down Expand Up @@ -511,7 +516,7 @@ Client::Impl::SessionOutcome Client::Impl::receive_session() {
consecutive_malformed_ = 0;
}
received_valid_record = true;
deadline = received_at + timeout;
deadline = detail::checked_deadline(received_at, timeout);
if (const auto outcome = handle_record(record, received_at)) {
return {*outcome, {}, received_valid_record};
}
Expand Down Expand Up @@ -631,6 +636,11 @@ Client::Impl::handle_record(const detail::RawRecord &record,
sample.torque = {record.tx / calibration.counts_per_torque_unit,
record.ty / calibration.counts_per_torque_unit,
record.tz / calibration.counts_per_torque_unit};
const auto finite = [](double value) { return std::isfinite(value); };
if (!std::all_of(sample.force.begin(), sample.force.end(), finite) ||
!std::all_of(sample.torque.begin(), sample.torque.end(), finite)) {
return SessionResult::SensorConfiguration;
}
sample.force_unit = calibration.force_unit;
sample.torque_unit = calibration.torque_unit;
sample.configuration_revision = configuration.revision;
Expand Down
Loading
Loading