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 CHANGELOG.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ English · [简体中文](CHANGELOG.md)

## Unreleased

- Synchronize source guides, backend contracts, API comments and validation inventory with native receive metadata, explicit frame formats and per-device Runtime ownership. Retain fixed-version evidence and distinguish Unreleased APIs from published SDKs.

- Add explicit per-message classic/FD selection while preserving backend defaults, native receive timestamp metadata and opt-in SocketCAN/PCAN receive-own configuration. Reject unsupported vendor/SLCAN echo opt-in, preserve driver counters without epoch conversion, and add encoding, rollback, metadata routing and deployment-evidence regressions. No hand/arm integration or physical hardware acceptance is included; consumers must rebuild with matching headers and library.

- Reject unsupported FIFO capacity units, send policies and deadline policies with `ValidationError` before configuration changes or send admission. Preserve valid defaults and existing signatures; add configuration, queued-latest, device/owned-operation and installed-consumer regressions.
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

## 未发布

- 同步源码指南、后端契约、接口注释和验证清单,修正原生接收元数据、显式帧格式及每设备 Runtime 所有权的过时说明;保留固定版本证据,并区分 Unreleased 接口与已发布 SDK。

- 新增逐帧显式经典/FD 类型、原生接收时间戳元数据和 SocketCAN/PCAN 自身发送帧接收选项,保留后端默认行为;vendor/SLCAN 拒绝不支持的回显开启,驱动计数器不推定纪元。补充编码、初始化回滚、元数据路由和部署证据回归。本次不接入 hand/arm、不计为实机验收,使用方须用匹配的头文件和库重新编译。

- 在配置修改或发送准入前,以 `ValidationError` 拒绝不支持的 FIFO 计量单位、发送策略和 deadline 策略;保留合法默认值和既有签名,增加配置、等待 latest、设备/已登记操作及安装消费者回归。

- 修正设备提交中显式全 Session 队龄检查被改写的问题;新增 `PendingAgeScope::kDefault`,保留设备局部与旧 Session 全局默认行为、既有枚举值和签名,增加作用域、已登记操作、替换、cooldown 及安装消费者回归。
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ See the [serial and RTU guide](docs/en/modbus-rtu.md).
Current source version: **0.3.3**, including optional serial and Modbus RTU modules.
Published v0.3.2 packages do not contain those modules. See the [latest published stable release](https://github.com/linker-bot/linkerbot-transport-cpp/releases/latest).
See [downloads, checksums and release documentation](docs/en/releases.md).
Source guides also cover [Unreleased changes](CHANGELOG.en.md); use the matching tag,
headers and bundled documentation to determine a published SDK's actual interfaces.

The canonical package is `linkerbot-transport`; former `linkerbot-comm` calls and
archives remain available as compatibility entries. See [name migration](docs/en/naming.md).
Expand Down
1 change: 1 addition & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ RTU 模块处理寄存器事务,设备寄存器映射和运动控制仍由上

当前源码版本:**0.3.3**,包含可选串口和 Modbus RTU 模块。已发布的 v0.3.2 包不包含这两项。下载[最新已发布的稳定版](https://github.com/linker-bot/linkerbot-transport-cpp/releases/latest)。
下载、校验及版本文档见[发行说明](docs/zh/releases.md)。
源码指南也涵盖[未发布变更](CHANGELOG.md);已发布 SDK 的实际接口以对应标签、头文件和随包文档为准。

规范包名为 `linkerbot-transport`;原 `linkerbot-comm` 查找入口和库文件继续兼容。
参见[命名迁移](docs/zh/naming.md)。
Expand Down
11 changes: 9 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

This index and the [English](../README.md) and [Chinese](../README.zh-CN.md) READMEs describe the
current implementation. Each guide has an English and a Chinese edition.
Historical hardware statements are separated in the validation guide.
Historical hardware statements are separated in the validation guide. Source guides
include the Changelog's Unreleased APIs; published SDKs must be checked against their
matching tag, headers and bundled documentation, rather than the version number alone.

## English guides

Expand All @@ -17,6 +19,8 @@ require parameters confirmed for the actual hardware; offline CI does not run th
- [Windows builds and standalone packages](en/windows-build.md)
- [Public API, threading and ownership](en/api.md)
- [Session, routing and lifecycle](en/session.md)
- [Per-device sessions and shared Runtime](en/device-sessions.md)
- [CAN frame selection, timestamps and receive-own messages](en/can-metadata.md)
- [Serial byte streams and Modbus RTU](en/modbus-rtu.md)
- Backends: [SocketCAN](en/socketcan.md), [MiniCANFD](en/minicanfd.md),
[PCAN](en/pcan.md), [SLCAN](en/slcan.md)
Expand All @@ -32,7 +36,8 @@ commands or validation scope must update both editions together. Keep identifier
and fixed-commit evidence unchanged when translating. Historical translations belong
under `docs/archive/`, with a historical-status notice and a link to this index.

本索引对应当前源码。先完成 FakeTransport 的离线构建和接入,再选择真实后端。
本索引对应当前源码,包含 Changelog 的 Unreleased 接口;已发布 SDK 应核对对应标签、头文件
和随包文档,不能只按版本号推定包含所有新能力。先完成 FakeTransport 的离线构建和接入,再选择真实后端。
文档中的真实设备示例需要按已确认的硬件参数调整,不会由离线 CI 自动执行。

## 维护状态
Expand All @@ -57,6 +62,8 @@ under `docs/archive/`, with a historical-status notice and a link to this index.

- [公共 API 与线程/所有权契约](zh/api.md):帧、传输层、Dispatcher、共享总线、Pool、取消和测试替身。
- [Session、路由与生命周期](zh/session.md):所有权、回执、共享、取消和恢复。
- [每设备会话与共享 Runtime](zh/device-sessions.md):独立生命周期与单发送器。
- [CAN 帧格式与接收元数据](zh/can-metadata.md):显式经典/FD、时间戳及回显限制。
- [通用串口与 Modbus RTU](zh/modbus-rtu.md):独立模块、寄存器事务、方向控制及恢复边界。
- [SocketCAN](zh/socketcan.md):Linux 网络接口、Classic/FD、超时及权限。
- [MiniCANFD](zh/minicanfd.md):平台 ABI、厂商库、枚举及资源重试。
Expand Down
22 changes: 17 additions & 5 deletions docs/en/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ header search paths and the C++17 requirement only; it does not replace the impl
| Reception and physical batch boundaries | `can::CANDispatcher` | Dedicated receive thread, subscription broadcasts and serialized physical transmit batches |
| Recoverable shared bus | `can::ManagedCANBus` | Stores a factory, explicit `start/stop/recover`, legacy transmit queue |
| In-process connection pool | `can::SharedCANBusPool`, `CANBusLease` | Same-key bus sharing; leases retain ownership needed to retry the final close |
| Device SDK session | `transport::Session`, `RouteInbox`, `SendReceipt` | Exact routing, one transmit scheduler, deadlines, cancellation and generation-based recovery |
| Shared multi-device runtime | `transport::TransportRuntime`, `DeviceSession`, `DeviceOperation` | Independent device registrations and lifetimes on one shared physical Session/writer |
| Direct Session and legacy sharing | `transport::Session`, `RouteInbox`, `SendReceipt` | Exact routing, one transmit scheduler, deadlines, cancellation and generation-based recovery |
| Offline testing | `can::FakeTransport` | In-memory I/O, error injection and synchronization gates; does not represent an adapter |

Device SDKs normally use Session. Use lower layers when implementing a backend or maintaining legacy code.
New multi-device integrations use `TransportRuntime` and one `DeviceSession` per
logical device on the existing Session writer; see [device sessions](device-sessions.md).
Direct Session use remains available. Use lower layers when implementing a backend
or maintaining legacy code.

## Frames and backend interpretation

Expand All @@ -31,12 +35,18 @@ are at most `0x7ff`; extended identifiers are at most `0x1fffffff` and require
`is_extended=true`. Classic frames carry at most 8 bytes and FD frames at most 64;
`is_brs=true` requires `is_fd=true`.

| Backend / mode | Transmit interpretation of `is_fd=false,is_brs=false` |
| Backend / mode | Transmit interpretation when `format` is unspecified and `is_fd=false,is_brs=false` |
| --- | --- |
| SocketCAN Classic, PCAN Classic, Lawicel Classic | Classic |
| SocketCAN FD, MiniCANFD | Compatibility behavior: FD without BRS |
| PCAN FD, CANable FD | Classic; set `is_fd=true` explicitly for FD |

`Message::format` explicitly selects classic/FD encoding without changing these
legacy defaults. Optional `timestamp` and `is_rx` preserve backend receive metadata;
TX ignores them. Clock domains and per-backend limits are specified in
[CAN metadata](can-metadata.md). Public structure additions require consumers to
rebuild with matching headers and libraries.

Backends may round lengths such as 9, 10 or 11 bytes up to a representable wire length
and pad with zeros. Upper-layer parsers must use the returned length and validate the
business format. Backend acceptance does not establish CAN acknowledgment or device execution.
Expand Down Expand Up @@ -99,8 +109,10 @@ to 512 frames. An executing batch does not occupy waiting-queue capacity but cou
`pending_tx`. Defaults are FIFO, priority 0 and no finite deadline. See the
[Session guide](session.md) for sharing, routing, receipts, recovery and exit contracts.

`transport::Clock` is `std::chrono::steady_clock`; deadlines and timestamps are not UTC
or Unix time. `CancellationSource` and its tokens share an atomic, one-way state.
`transport::Clock` is `std::chrono::steady_clock`; Session deadlines, receipt times
and `RoutedMessage::received_at` are not UTC or Unix time. `Message::timestamp` is
separate backend metadata whose clock and unit must be inspected.
`CancellationSource` and its tokens share an atomic, one-way state.
Cancellation is repeatable but cannot be reversed. Cancellation, deadline expiry or a wait
timeout does not establish that a frame already passed to a driver was not delivered.
Inspect `sent_frames`, `current_frame_uncertain` and device state at the business layer.
Expand Down
10 changes: 7 additions & 3 deletions docs/en/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,9 @@ The separate `C++ Report Card` workflow runs
[cppreportcard-action](https://github.com/soulteary/cppreportcard-action) v1.0.2,
pinned to a full commit SHA, on Linux ARM64. It runs clang-format 18, clang-tidy 18
and Cppcheck in `full` mode after building the project, quickstart, installed
consumer and shared-library consumer. Their four compilation databases are combined
so the standalone consumers are analyzed with the main project's sources and tests.
consumer, shared-library consumer and installed-library Python validation probe.
Their five compilation databases are combined so all standalone consumers and the
probe are analyzed with the main project's sources and tests.

The configuration is `.cppreportcard.json`. Platform-dependent exclusions are
`tests/vendor_fixture_dependency.cpp` (Windows-only), `validation/pcan_header_abi.cpp`
Expand All @@ -281,7 +282,10 @@ The report uses `clang-analyzer-*` and `bugprone-*`; it does not replace the
existing style workflow's different rule set or platform checks.

`validation/prepare-report-db.py` merges the databases and exposes project
`-isystem` directories as `-I` directories for Cppcheck. Through
`-isystem` directories as `-I` directories for Cppcheck. The workflow also records
the compiler's actual target macros. Cppcheck preincludes the installed multiarch
Python configuration to traverse CPython's same-name forwarding header; no probe
source or preprocessor error is excluded. Through
`validation/cppcheck-report.sh`, Cppcheck uses its `std` library model and
`validation/catch2-cppcheck.cfg` for the Catch2 macros used by the project.
The wrapper suppresses only
Expand Down
2 changes: 2 additions & 0 deletions docs/en/can-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ frames stay classic). Existing `is_fd`/`is_brs` callers need no changes. Receive
frames keep the existing flags and leave the new TX override unspecified. A
forwarder that needs classic-on-FD must explicitly choose the classic override.
`Session::submit` validates the override before admission or latest replacement.
Receive route keys use the arbitration ID and standard/extended-ID flag, not this
TX classic/FD override.

## Receive timestamps

Expand Down
11 changes: 11 additions & 0 deletions docs/en/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ bytes and downloads them to compare hashes. It alone has repository write permis
automatically publishes or moves tags. Inspect an incomplete draft before retrying; released
bytes require a new version instead of replacement. Changing these workflows does not itself create a tag or Release.

## Python pre-integration validation

The separate `python-preintegration.yml` workflow builds an isolated CPython probe
against the installed native library. It tests conventional GIL-enabled CPython
3.10–3.13 on Linux x86_64/ARM64, Windows x64 with 3.12 and Windows ARM64 with 3.13.
The source fixture is `validation/python-preintegration`; it is not a production
binding or a wheel. Subinterpreters and free-threaded builds are rejected.
Allocation/receipt, callback, lifecycle and deployment-gate checks do not establish
actual hand/arm Runtime sharing or hardware compatibility. See [CAN metadata](can-metadata.md)
for backend-specific capabilities and physical validation limits.

## CI timing and reproducible comparisons

See the [measured results and limitations](ci-performance.md), including all baseline and
Expand Down
4 changes: 2 additions & 2 deletions docs/en/device-sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ The Python source baselines use a shared hand domain of 256 request batches and

## Routes and diagnostics

`subscribe_routes()` owns exact ID/format pairs. `subscribe_rules()` owns masked ID/format ranges, including extended A7 Lite routing. Rules preserve the original frame and record their logical key separately. Overlap is rejected using actual wire ranges, including exact-versus-masked conflicts. Group registration rolls back after allocation failure. Masked routes cannot use the legacy global extractor.
`subscribe_routes()` owns exact ID/standard-versus-extended-ID pairs. `subscribe_rules()` owns masked ID ranges with the same extended-ID flag, including extended A7 Lite routing. Rules preserve the original frame and record their logical key separately. Overlap is rejected using actual wire ranges, including exact-versus-masked conflicts. Group registration rolls back after allocation failure. Masked routes cannot use the legacy global extractor.

`observe_rules(rules, capacity)` provides bounded read-only discovery observation. It does not take route ownership or ordinary receive-budget slots; a full observer drops its own copy. Normal owners receive their frame first. Discovery requests still use the same device writer. Observation inbox health records its own backlog and drops.

`max_route_keys` bounds registered rules including observations; `max_pending_rx` bounds the ordinary receive backlog. Zero preserves legacy unlimited behavior. Inbox overflow drops the new frame. `compatibility_sequence` increments after matching and before queue offer, so overflow leaves gaps; it resets on physical invalidation. The existing C++ sequence remains unchanged. Device fault reasons are retained by inboxes; legacy inbox close exceptions retain their existing contract.

Channel health includes route count, receive backlog/high-water mark, routed frames, stale frames, registration quota rejections and close drops. Device health reports admission state, generation, instance, registered operations, send-error streak, queued FIFO frames/latest slots, in-flight jobs and oldest owned work.

This transport implementation does not provide Python bindings, Python Future execution, protocol transactions, SDK migration or motor safety policy. Hardware timestamps and arbitrary Python backend plugins are not provided by these APIs. Software tests do not establish hardware or firmware compatibility; deployments need the device/driver/topology acceptance checks described in the migration plan.
This transport implementation does not provide Python bindings, Python Future execution, protocol transactions, SDK migration or motor safety policy. Native receive timestamps and backend-specific echo options are described in [CAN metadata](can-metadata.md). Arbitrary Python backend plugins are not implied by the native backends. Software tests do not establish hardware or firmware compatibility; deployments need the device/driver/topology acceptance checks described in the migration plan.
7 changes: 6 additions & 1 deletion docs/en/minicanfd.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,19 @@ confirm indices, bitrates and vendor fields against enumeration and vendor docum
| `frame_type` | `0x04`; retained for aggregate-initialization source compatibility only; unused by TX or `CANFD_Init` |
| `send_timeout_ms` | 10 ms, accepts 0–100 ms |
| `receive_timeout_ms` | 10 ms, accepts 0–10 ms |
| `receive_own_messages` | `false`; `true` is rejected before library loading or channel initialization |
| Native receive buffer per call | Requests at most 64 frames |
| Native send batch per call | At most 100 frames |

Default arbitration/data rates are 1 Mbit/s / 5 Mbit/s. Standard/extended IDs, FD and BRS are
handled per message. Untagged messages on an FD Transport retain the existing FD-without-BRS
behavior. The upper-layer device protocol selects IDs and BRS; the Transport does not infer flags
from model names. `Message.dlc` counts 0–64 bytes and is converted to the vendor's 0–15 DLC code.
Unrepresentable lengths round up and are zero-padded. `Config::frame_type` does not override send flags.
Unrepresentable lengths round up and are zero-padded. `Config::frame_type` does not override send flags. Set `Message::format` to
`FrameFormat::kClassic` for an explicit classic frame on the FD channel, with at most
eight bytes and no FD/BRS flags. Receive timestamps preserve the raw 32-bit vendor
counter; units, epoch and wrap extension are not inferred. Direction remains unknown,
and receive-own opt-in is unsupported by this ABI. See [CAN metadata](can-metadata.md).

A direct `write_batch()` call with more than 100 frames synchronously throws `ValidationError`.
When Session selects a native batch with `gap=0`, it passes the entire `MessageBatch` to that
Expand Down
7 changes: 6 additions & 1 deletion docs/en/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ Expose only the interfaces the container uses: a visible CAN network interface f

## Sharing, shutdown, and failed cleanup

Devices can share a Session, but it is not an independent connection handle for each caller. Any sharer calling `close()` closes the entire Session, cleans up resources held by that Session, and invalidates the other sharers' inboxes. Ownership of an external bus or borrowed dispatcher does not transfer to the Session. When one device finishes, close its inbox and cancel its endpoint first; a common owner should close the Session last.
For independent device lifetimes, use `TransportRuntime` with one `DeviceSession`
per device. Closing a device releases its own work, subscriptions and counted lease;
peers remain connected, and the last device releases the channel. Retry ownership
and coordinated recovery are described in [device sessions](device-sessions.md).

For legacy callers sharing a raw Session, it is not an independent connection handle for each caller. Any sharer calling `close()` closes the entire Session, cleans up resources held by that Session, and invalidates the other sharers' inboxes. Ownership of an external bus or borrowed dispatcher does not transfer to the Session. When one device finishes, close its inbox and cancel its endpoint first; a common owner should close the Session last.

Shutdown order: stop producing application tasks → cancel endpoints no longer needed → check in-flight receipts and device state → close inboxes/Session → handle retryable cleanup errors → release borrowed dispatchers or external buses. Do not synchronously call Session `close()`/`recover()` from callbacks. Before destroying a borrowed dispatcher, call `Session::drain_deferred_cleanup()` from an external thread to wait for deferred cleanup.

Expand Down
Loading
Loading