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: 1 addition & 1 deletion .github/workflows/release-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ name: Release packages
expected_version:
description: 'Hand SDK version (transport may use a compatible newer patch)'
required: true
default: '0.3.6'
default: '0.3.7'

permissions:
contents: read
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

[English](CHANGELOG.md) | [简体中文](CHANGELOG.zh-CN.md)

## 0.3.7

- Add publication-based channel sequences, identity sample metadata, filtered retained-cache waits, firmware-optional query degradation and unified angle feedback policies to the explicit Python runtime policy.
- Correct matching tactile response validation and force aggregation; add independent L20Lite scalar queries in that policy.
- Preserve native defaults and the transport v0.3.4 minimum. Rebuild SDK and consumers. Hardware acceptance remains separate; see [release notes](RELEASE_NOTES.md).

## 0.3.6

- Add an opt-in Python runtime policy for coalesced reads, response-stage windows, strict matching responses, close terminal outcomes and bounded explicit recovery.
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

[English](CHANGELOG.md) | [简体中文](CHANGELOG.zh-CN.md)

## 0.3.7

- 为显式 Python 运行策略补齐按发布推进的通道序号、身份采样元数据、过滤反馈的缓存等待、固件可选查询降级和统一角度反馈策略。
- 修正匹配触觉响应的校验和力数据合并;在该策略下增加 L20Lite 独立标量查询。
- 保留原生默认行为和 transport v0.3.4 最低依赖。SDK 与消费者需要重新编译,实机验收单独进行。详见[发行说明](RELEASE_NOTES.md)。

## 0.3.6

- 新增显式 Python 运行时策略,覆盖读取合并、发送后的响应窗口、匹配响应校验、关闭终态及有界显式恢复。
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16)

project(linkerbot-hand-cpp
VERSION 0.3.6
VERSION 0.3.7
DESCRIPTION "LinkerBot dexterous hand C++ SDK"
LANGUAGES CXX
)
Expand Down
2 changes: 1 addition & 1 deletion README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ This installs the SDK and communication library into the local `install/` direct
Catch2 and vendor development packages are not required. Run the remaining commands from
this repository's root directory.

Hand SDK 0.3.6 requires transport 0.3.4 or newer in the 0.3 minor line,
Hand SDK 0.3.7 requires transport 0.3.4 or newer in the 0.3 minor line,
for source builds, prebuilt packages and installed consumers.

To reuse a prebuilt transport v0.3.4 SDK, configure with
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ This installs the SDK and communication library into the local `install/` direct
Catch2 and vendor development packages are not required. Run the remaining commands from
this repository's root directory.

Hand SDK 0.3.6 requires transport 0.3.4 or newer in the 0.3 minor line,
Hand SDK 0.3.7 requires transport 0.3.4 or newer in the 0.3 minor line,
for source builds, prebuilt packages and installed consumers.

To reuse a prebuilt transport v0.3.4 SDK, configure with
Expand Down
12 changes: 12 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Release notes

## 0.3.7

- Extend the explicit `RuntimeBehavior::kPython` policy with publication-based channel sequences and coherent retained snapshots. Cache readers do not advance acquisition counters. Identity reads carry independent sample metadata; closed Python-policy caches retain identity evidence with invalid metadata.
- Add `wait_for_cached_snapshot(previous, timeout, wake_on_command_feedback)` without overloading the existing two-argument wait signature. Filtered waits use the last non-command publication, retain command feedback in the current cache, and wake when their device generation ends. These waits perform no device I/O.
- Reject matching malformed Classic tactile rows in the Python policy, including invalid lengths, row markers and conflicting duplicates. Unrelated routes and opcodes remain unmatched. Correct force aggregation so one query replaces its own fields and scalar raw data follows the latest scalar query while matrix evidence from other queries remains available.
- Read L20Lite fingertip scalar categories independently in the Python policy. Track firmware-optional failures per channel and generation: only sent, unverified optional queries can degrade after repeated response/protocol failures; a previously successful channel is not disabled by later response loss. Disable only that channel and restore probing after explicit recovery.
- Add query-only, command-feedback-only and adaptive angle feedback policies to unified polling. Only verified current-state command feedback fulfills the requested angle rate. Equivalent plans are no-ops. Polling uses the existing native runtime and transport Session; no second physical send queue is added.

Native behavior remains the default. The policy covers Classic L6, O6, L20Lite, L20, L30 and explicitly selected O30 V25. Existing C++ method signatures remain available; public metadata and configuration additions require rebuilding the SDK and downstream consumers with matching headers and libraries. The minimum and paired transport remain v0.3.4 (`0e190032d65ed0b315337b45b3779cf4f650ed68`). Binary compatibility with earlier headers is not claimed.

Validation uses offline FakeTransport regressions, Python reference contracts, native/source/prebuilt/installed consumers, sanitizers, complete static analysis and the twelve-target release matrix. No new physical-device acceptance is claimed. Adapter/driver/firmware, physical timestamps, deployed tactile layouts, mixed CAN/FD channels and long-duration multi-device behavior require separate hardware validation. O20, Python bindings, automatic reconnect and hand/arm integration are outside this patch. Extended identity raw/version field parity and O30 field-by-field identity requests remain separate work.

## 0.3.6

- Add an explicit `RuntimeBehavior::kPython` policy for the unified facade. The native policy remains the default. Classic L6, O6, L20Lite, L20, L30 and explicit O30 V25 are in scope; L6 HOP and mounted O30 retain their existing native path.
Expand Down
17 changes: 16 additions & 1 deletion docs/en/guides/python-behavior-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Still required before replacing the accepted Python implementation:
- A pinned business configuration and same-input cross-language acceptance
corpus for every deployed model, unit/range boundary and fault response.
- Deployed-configuration acceptance for the opt-in transaction policy below,
plus adaptive feedback and automatic reconnect parity. Native defaults remain
plus automatic reconnect parity and the remaining deployment-specific behavior corpus. Native defaults remain
separate from the Python policy; passing offline cases is not complete parity.
- A Python binding proving receipt retention on allocation/wrapping failure,
GIL/callback/destructor/interpreter-exit safety, exception conversion and
Expand Down Expand Up @@ -121,3 +121,18 @@ runtime.recovery_attempts = 3;
runtime.recovery_timeout = 5s;
runtime.recovery_quiet = 200ms;
```


## Third compatibility patch (0.3.7)

The following additions apply to the explicit Python runtime policy; native defaults remain unchanged.

- Complete telemetry publications advance their own channel sequence even when no caller reads a snapshot. A coherent publication view supplies sample sequence, generation, receive time and response/report/command delivery metadata. Global snapshot revisions also include identity and observed capability changes. Repeated cache reads do not create acquisitions.
- `DeviceIdentity::meta` carries its own identity-read sequence and monotonic collection metadata. A successful `refresh_device_info()` advances identity without advancing telemetry channels. Retained caches preserve identity evidence with unavailable metadata after generation loss. This does not add the remaining Python raw/version detail fields.
- `wait_for_cached_snapshot(previous, timeout, wake_on_command_feedback = true)` reads retained state and performs no device I/O. Filtering suppresses command-only wakeups, not cache retention. A non-command update returns its coherent revision, including command data that preceded that revision. Timeout returns the current cache; close or recovery releases old-generation waiters. The original wait signature remains unambiguous; native wait behavior is retained.
- Matching Classic tactile rows validate length and compact/nibble row indices before assembly. Conflicting repeats fail the transaction without replacing the previous complete sample. Complete identical duplicates do not fabricate a new acquisition. Unrelated routes/types/opcodes stay unmatched.
- Force aggregation replaces all fields belonging to the completed query while retaining other queries and their original collection metadata. Scalar-only aggregate raw data follows the latest scalar query. L20Lite Python-policy scalar getters issue only their selected category query (0x20..0x23); the native aggregate query remains available on the native path.
- Firmware-optional observations belong to one device generation. Only a physically sent optional query that has not succeeded in that generation can degrade. Three timeout/protocol failures disable that query; an explicit unsupported response can disable it immediately. Transport failure and expired unsent callers do not establish unsupported firmware. Capability queries, force status and polling reflect the observation; other channels remain usable. Explicit recovery clears those observations. Required initialization remains required.
- `PollingChannel::angle_feedback` selects `kQueryOnly`, `kCommandFeedbackOnly` or `kAdaptive`; the Python-policy default is adaptive. Command-only mode requires a declared verified current-state response and never repeats motion targets. Query-only mode does not use command feedback to postpone queries. Adaptive mode postpones only redundant angle queries after verified feedback. Feedback, completed-read, timeout and missed-deadline metrics remain distinct. Reapplying an equivalent plan preserves deadlines and metrics. All physical frames still pass through the existing Session.

Python bindings must map retained snapshot and filtered wait APIs explicitly. GIL, callback, destructor/interpreter-exit and allocation-failure proof, wall-clock conversion and shared hand/arm channel integration are not provided by this native patch. Hardware acceptance remains separate. O20 and automatic reconnect are not included.
2 changes: 1 addition & 1 deletion docs/en/guides/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ availability of proprietary ARM64 drivers.

Hand SDK packages contain both hand and paired transport static libraries, headers, CMake packages,
documentation and read-only command-line examples. Test executables and proprietary drivers/libraries
are excluded. Versions remain independent. Hand 0.3.6 requires transport 0.3.4 or newer in
are excluded. Versions remain independent. Hand 0.3.7 requires transport 0.3.4 or newer in
the 0.3 minor line in source, prebuilt and installed-consumer builds.


Expand Down
2 changes: 1 addition & 1 deletion docs/en/guides/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ A parent-provided `linkerbot::comm` target is reused and version-checked.
The legacy empty `LINKERBOT_COMM_SOURCE_DIR=` still selects package discovery through
`CMAKE_PREFIX_PATH` in AUTO mode. Configure logs identify the selected dependency.
Use separate build directories when switching dependencies to avoid stale CMake caches.
Hand 0.3.6 requires transport 0.3.4 or newer in the 0.3 minor line. Source,
Hand 0.3.7 requires transport 0.3.4 or newer in the 0.3 minor line. Source,
prebuilt and installed-consumer checks reject older patches and other minor lines.
For paired development, keep the source commit pinned in README and CI; record the
actual revision and configuration in test reports.
Expand Down
15 changes: 15 additions & 0 deletions docs/zh/guides/python-behavior-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,18 @@ runtime.recovery_attempts = 3;
runtime.recovery_timeout = 5s;
runtime.recovery_quiet = 200ms;
```


## 第三批兼容补全(0.3.7)

本批新增运行行为仅用于显式 Python 策略,原生默认策略不变。

- 通道序号按完整样本发布次数推进,不依赖读取快照的次数。统一发布视图提供序号、连接代次、单调采样时间及查询/报告/命令反馈来源。身份刷新和能力观察也会产生可见快照修订。
- `DeviceIdentity::meta` 提供独立身份序号和采样元数据。身份刷新不推进遥测通道序号;连接代次失效后,保留缓存中的身份证据携带不可用元数据。本批不补齐所有 Python 原始身份及版本详情字段。
- 新增独立命名的 `wait_for_cached_snapshot(previous, timeout, wake_on_command_feedback = true)`。过滤只抑制纯命令反馈引起的唤醒,不删除当前缓存中的反馈。非命令发布返回该修订的一致视图;等待超时返回当前缓存,关闭或恢复释放旧代次等待者。原有等待方法签名与原生行为保持兼容,等待不产生设备 I/O。
- 匹配的 Classic 触觉行校验长度、紧凑或高半字节行号及冲突重复。异常不能替换上次完整样本;无关路由、帧类型和操作码仍忽略。
- 力数据按查询归属替换字段,保留其他查询的采样证据和各自时间。纯标量聚合的 raw 对应最新标量查询。L20Lite Python 策略使用独立的 0x20..0x23 类别查询,原生聚合查询不变。
- 固件可选查询仅在当前设备连接代次内降级。只有已实际发送、且该代次尚未成功的可选查询,才累计超时或协议错误;三次失败停用该查询,明确不支持的响应可立即停用。传输故障和未发送调用超时不构成固件不支持的证据。能力、力字段状态和轮询同步反映观察,其他通道继续可用;显式恢复后重新探测,必需初始化项目仍然必需。
- 统一角度轮询提供 query-only、command-feedback-only 和 adaptive 三种策略,Python 策略默认 adaptive。只有已声明为实际状态的命令反馈才能满足采样需求;command-only 不补发运动目标,query-only 不因命令反馈推迟查询。相同计划不重置期限或指标,物理帧继续交给既有 Session 发送。

绑定层仍需完成 API、异常、不可变对象及墙钟转换映射,并证明内存分配失败、GIL、回调、析构和解释器退出的行为。本批不接入 hand/arm,不包含 O20、自动重连或新的实机验收。实际后端、适配器/驱动、固件、触觉布局、时间戳及多设备长时间运行需独立验收。
2 changes: 1 addition & 1 deletion docs/zh/guides/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ libstdc++ ABI1 和 C++17。Windows 使用原生架构 runner 与 v143 14.44 工

产物是开发 SDK,Hand 包包含 hand 与配套 transport 静态库、头文件、CMake 包和文档,
保留只读命令行示例;不包含测试程序、厂商驱动或厂商库。
两个项目版本独立。hand 0.3.6 的源码、预构建及安装消费者均要求 transport 0.3.x 且不低于 0.3.4。
两个项目版本独立。hand 0.3.7 的源码、预构建及安装消费者均要求 transport 0.3.x 且不低于 0.3.4。


配套源码固定提交和预构建下载统一使用 v0.3.4 发行版。transport 公共结构有新增字段,
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/guides/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
父项目已经提供 `linkerbot::comm` 时,复用父项目的目标,并检查版本。
显式设置旧参数 `LINKERBOT_COMM_SOURCE_DIR=` 仍可在 AUTO 下通过 `CMAKE_PREFIX_PATH`
使用安装包。配置日志会显示实际选择的依赖;切换依赖时使用独立构建目录,避免旧 CMake 缓存。
hand 0.3.6 要求 transport 0.3.x 且不低于 0.3.4。源码、预构建及安装消费者测试均拒绝更低补丁版本和其他 minor 版本。
hand 0.3.7 要求 transport 0.3.x 且不低于 0.3.4。源码、预构建及安装消费者测试均拒绝更低补丁版本和其他 minor 版本。
源码开发继续使用 README 与 CI 固定的提交,并在测试报告中记录实际版本和构建配置。

### 从源码构建
Expand Down
6 changes: 6 additions & 0 deletions include/linkerbot/hand/api/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ namespace linkerbot::hand {
[[nodiscard]] HandSnapshot wait_for_snapshot(const HandSnapshot &previous,
std::chrono::milliseconds timeout) const;

// Python-compatible retained-cache wait. Filtering excludes command-only
// publications from wakeups, but does not remove them from the cache.
[[nodiscard]] HandSnapshot wait_for_cached_snapshot(const HandSnapshot &previous,
std::chrono::milliseconds timeout,
bool wake_on_command_feedback = true) const;

void start_polling();
void start_polling(const PollingPlan &plan);
void stop_polling();
Expand Down
2 changes: 2 additions & 0 deletions include/linkerbot/hand/api/events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace linkerbot::hand {
struct PollingChannel {
TelemetrySemantic semantic = TelemetrySemantic::kAngles;
std::chrono::milliseconds interval{100};
// Used for angles by the explicit Python runtime policy only.
AngleFeedbackPolicy angle_feedback = AngleFeedbackPolicy::kAdaptive;
};

using PollingPlan = std::vector<PollingChannel>;
Expand Down
5 changes: 5 additions & 0 deletions include/linkerbot/hand/capabilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace linkerbot::hand {

enum class HandSide : uint8_t { kUnknown, kLeft, kRight };

enum class AngleFeedbackPolicy : uint8_t { kQueryOnly, kCommandFeedbackOnly, kAdaptive };

enum class TransportKind : uint8_t { kCan, kCanFd };

enum class Confidence : uint8_t { kMeasured, kDocumented, kApproximate, kConflicting, kUnknown };
Expand Down Expand Up @@ -243,6 +245,7 @@ namespace linkerbot::hand {
std::optional<std::string_view> raw_unit;
std::optional<std::string_view> raw_encoding;
std::optional<std::string_view> raw_scale_note;
bool firmware_unsupported = false;
};

// Capability strings are non-owning views into SDK-owned static profile data.
Expand All @@ -258,6 +261,8 @@ namespace linkerbot::hand {
CapabilitySchemaVersion schema_version = kCapabilitySchemaVersion;
// Runtime descriptor observations; views still reference static profile names.
std::vector<ForceFieldCapability> observed_force_fields;
// Observed only in the current verified connection generation.
std::vector<TelemetrySemantic> firmware_unsupported_telemetry;

[[nodiscard]] bool supports(CommandSemantic semantic) const noexcept;
[[nodiscard]] bool supports(TelemetrySemantic semantic) const noexcept;
Expand Down
3 changes: 3 additions & 0 deletions include/linkerbot/hand/health.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <linkerbot/can/health.hpp>
#include <linkerbot/hand/capabilities.hpp>
#include <linkerbot/hand/errors.hpp>
#include <linkerbot/transport/session.hpp>

Expand Down Expand Up @@ -98,6 +99,8 @@ namespace linkerbot::hand {
DataAge data_age;
LatencyMetrics latency;
QueueMetrics queue;
uint64_t command_feedback_samples = 0;
std::optional<AngleFeedbackPolicy> feedback_policy;
};

// One independently scheduled command family. Counters count logical
Expand Down
3 changes: 3 additions & 0 deletions include/linkerbot/hand/identity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <string_view>
#include <vector>

#include <linkerbot/hand/api/readings.hpp>
#include <linkerbot/hand/capabilities.hpp>
#include <linkerbot/hand/errors.hpp>

Expand Down Expand Up @@ -38,6 +39,8 @@ namespace linkerbot::hand {
std::optional<std::string> hardware_version;
std::optional<std::string> mechanical_version;
std::chrono::steady_clock::time_point timestamp;
// Populated by the explicit Python runtime policy; timestamp is monotonic.
SampleMeta meta;
};

// Python classic_can.parse_factory_identity contract. No protocol I/O.
Expand Down
Loading