Metadata-Version: 2.4
Name: python-can-canpal
Version: 0.1.0rc6
Summary: CANPAL USB and TCP backend for python-can
License-Expression: LicenseRef-CANPAL-Runtime-Bundle
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Requires-Python: <3.13,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: LICENSE-BUNDLE
License-File: LICENSE-CANPAL-SDK
Requires-Dist: python-can==4.6.1
Requires-Dist: pyserial==3.5
Provides-Extra: test
Requires-Dist: pytest<10,>=8; extra == "test"
Requires-Dist: build<2,>=1; extra == "test"
Requires-Dist: twine<8,>=6; extra == "test"
Dynamic: license-file
License-File: src/canpal/_native/licenses/component-sources.json
License-File: src/canpal/_native/licenses/gcc-COPYING.LIB.txt
License-File: src/canpal/_native/licenses/gcc-COPYING.RUNTIME.txt
License-File: src/canpal/_native/licenses/gcc-COPYING.txt
License-File: src/canpal/_native/licenses/gcc-COPYING3.LIB.txt
License-File: src/canpal/_native/licenses/gcc-COPYING3.txt
License-File: src/canpal/_native/licenses/LICENSE-CANPAL-SDK
License-File: src/canpal/_native/licenses/mingw-build-info.txt
License-File: src/canpal/_native/licenses/mingw-w64-COPYING.MinGW-w64-runtime.txt.txt
License-File: src/canpal/_native/licenses/mingw-w64-COPYING.MinGW-w64.txt.txt
License-File: src/canpal/_native/licenses/mingw-w64-COPYING.txt
License-File: src/canpal/_native/licenses/mingw-w64-ddk-readme.txt.txt
License-File: src/canpal/_native/licenses/mingw-w64-direct-x-COPYING.lib.txt
License-File: src/canpal/_native/licenses/mingw-w64-direct-x-readme.txt.txt
License-File: src/canpal/_native/licenses/mingw-w64-DISCLAIMER.PD.txt
License-File: src/canpal/_native/licenses/mingw-w64-DISCLAIMER.txt
License-File: src/canpal/_native/licenses/qt-licenses.json
License-File: src/canpal/_native/licenses/qt-qcalendar-license-correction.json
License-File: src/canpal/_native/licenses/qt-qcalendar-license-correction.patch
License-File: src/canpal/_native/licenses/qtbase-LICENSE.GPL3
License-File: src/canpal/_native/licenses/qtbase-LICENSE.LGPL3
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-double-conversion-LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-double-conversion-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-easing-LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-easing-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-forkfd-LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-forkfd-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-freebsd-LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-freebsd-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-gradle-LICENSE-GRADLEW.txt
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-gradle-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-md4-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-md5-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-pcre2-LICENCE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-pcre2-LICENCE-SLJIT
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-pcre2-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-rfc6234-LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-rfc6234-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-sha1-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-sha3-BRG_ENDIAN_LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-sha3-CC0_LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-sha3-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-tinycbor-LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-tinycbor-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-zlib-LICENSE
License-File: src/canpal/_native/licenses/qtbase-src-3rdparty-zlib-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-QBIG5CODEC_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-QBKCODEC_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-QEUCJPCODEC_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-QEUCKRCODEC_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-QJISCODEC_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-QSJISCODEC_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-corelib-codecs-QTSCIICODEC_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-io-PSL-LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-io-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-corelib-kernel-QEVENTDISPATCHER_CF_LICENSE.txt
License-File: src/canpal/_native/licenses/qtbase-src-corelib-kernel-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-corelib-text-qt_attribution.json
License-File: src/canpal/_native/licenses/qtbase-src-corelib-text-UNICODE_LICENSE.txt
License-File: src/canpal/_native/licenses/qtserialport-LICENSE.GPL3
License-File: src/canpal/_native/licenses/qtserialport-LICENSE.LGPL3
License-File: src/canpal/_native/licenses/redistribution.json
License-File: src/canpal/_native/licenses/RUNTIME-REPLACEMENT.zh-CN.md
License-File: src/canpal/_native/licenses/THIRD-PARTY-SOURCES.md
License-File: src/canpal/_native/licenses/winpthreads-COPYING.txt

# python-can-canpal

CANPAL USB and TCP client backend for [python-can](https://python-can.readthedocs.io/).

**0.1.0rc6 is an experimental preview. Full hardware release acceptance is incomplete.**
Use it for evaluation on an isolated test bench. It is not the qualified 0.1.0 release.

## Install

Requires Python 3.12, python-can 4.6.1 and pySerial 3.5. Platform wheels include
the pinned CANPAL native SDK and required non-system runtime libraries.

| Platform | Python process |
| --- | --- |
| Windows 11 x64 | 64-bit or 32-bit (WOW64) |
| Ubuntu 24.04 x86_64 | 64-bit |

Linux wheels require glibc 2.39 or newer and the system GLib runtime. On minimal
Ubuntu 24.04 installations, install `libglib2.0-0t64` before using the backend.
The standard system libraries `libstdc++6` and `zlib1g` must also be present.

The Windows `win32` wheel requires 32-bit Python; it does not qualify a 32-bit
Windows OS. Windows x86 installation was verified with Smart App Control off;
enforced application control previously blocked the unsigned native runtime.
ARM64 requires Python 3.12 and glibc 2.39 or newer. The vendor Ubuntu 22.04
image on the tested Firefly board cannot run this wheel directly; tests used
an isolated Ubuntu 24.04 user space on its native ARM CPU and hardware drivers.
These ARM64 results apply to rc5. ARM64 rc6 is withheld while its new-feature
acceptance is deferred; use `python-can-canpal==0.1.0rc5` on ARM64, without the
new dual-channel, TX echo or diagnostic features. ARM32, Windows ARM64 and
Linux i686 are not part of this release.

```text
python -m pip install --only-binary=:all: python-can-canpal==0.1.0rc6
```

The MIT license applies to the Python plugin, tests and documentation. The
native SDK and bundled third-party libraries have separate terms included in
the platform wheel. Firmware and OS device drivers remain separate.
The bundle uses QtCore, QtNetwork and QtSerialPort under LGPLv3, with their
third-party notices. The SDK is covered by LICENSE-CANPAL-SDK version 0.2.
You may modify and replace compatible LGPL libraries under their terms; the
package does not lock their release hashes at runtime. Replacement instructions,
component licenses and source details are included in `canpal/_native/licenses/`.

[Corresponding third-party sources](https://pan.baidu.com/s/1mi2pbxI) are under
`CANPAL分析仪/二次开发/python-can-canpal/`:

| Bundled platform | Directory and archive | SHA256 |
| --- | --- | --- |
| Windows x64 / Linux x86_64 (unchanged rc3 binaries) | `0.1.0rc3/third-party-sources/python-can-canpal-0.1.0rc3-third-party-sources.zip` (235600943 bytes) | `8919f717e793b6c021fc7cb35fe0cdd788383bbb554a4021320036c658caec4a` |
| Windows x86 / Linux ARM64 | `0.1.0rc4/third-party-sources/python-can-canpal-0.1.0rc4-new-architectures-third-party-sources.zip` (234709370 bytes) | `f34ea2499015d7165f0284a85cf6ae53f0b5f9e51d5f8f2ce450dfea95dbabc8` |

rc6 reuses the rc5 native binaries and these exact source archives.
The wheel also includes the upstream Qt QCalendar LGPL-header correction.
For source requests or download problems, contact
[yongxiang.wu@hotmail.com](mailto:yongxiang.wu@hotmail.com), specifying version
and platform. The installed wheel includes `canpal/_native/licenses/THIRD-PARTY-SOURCES.md`.

## Hardware evidence and limits

The hardware records below describe rc5 and earlier qualification. They do not
establish fresh rc6 feature coverage. X5 0x0333 / X6 0x0128 separately introduced
per-channel FIFO transmission with in-place retries; see the
[firmware delivery scope](docs/TX-ORDER-DELIVERY-PLAN.zh-CN.md).
The new TX echo, diagnostics and retry options have their own acceptance records
in [release testing](docs/RELEASE.md). Existing platform/reference limits remain.

Earlier Windows performance tests of rc5 found same-ID frame reordering in
X6-to-X5 traffic during simultaneous bidirectional transmission, using both
native C++ and python-can on x64/x86. X5 WLAN and higher USB loads also showed
frame loss. Linux/ARM64 performance tests were not run in this round. See the
[performance results and measured limits](docs/PERFORMANCE-RESULTS-20261011.zh-CN.md).

The following x64 evidence was obtained with rc3 and checked again for rc4.
rc5 retains the rc4 Windows x64/x86 and Linux x86_64 runtime bytes, with only
version and documentation changes. Their recorded qualification is reused;
this does not imply a fresh run of every historical hardware test.

| Environment | Preview evidence |
| --- | --- |
| Windows 11 x64 | X5 classic CAN/RTR and X6 classic CAN/RTR/ISO FD over USB and explicit-IP WLAN TCP |
| Windows, multiple devices | Two USB devices, X5 USB + X6 WLAN, X5 WLAN + X6 USB; five reopen cycles per device/transport |
| Ubuntu 24.04 x86_64 in Hyper-V | Native Linux SDK with two USB devices through USB/IP; ttyACM number swap with UID selection; TCP checks through temporary SSH forwarding via Windows WLAN |

Both devices had CAN1 wired to their own CAN2 on separate physical buses.
These checks do not use an independent reference CAN interface. Classic checks
covered standard/extended frames and RTR DLC 0–8 in both directions. X6 FD checks
covered lengths 0–64 and BRS off/on. The current SDK checks verified 19,680 frames
and 20 Windows reopen cycles. Of these, 4,384 frames were verified on Linux USB
using usbipd-win 5.3.0 and Linux CDC ACM. Reversing attachment order swapped
ttyACM0/ttyACM1 while UID selection still found the correct X5 and X6.

SDK software regressions passed 170 assertions on each of Windows x64, Windows
x86, and Ubuntu x86_64, plus two Linux ThreadSanitizer checks. Windows x86 plugin candidate checks additionally verified USB, both mixed
USB/WLAN directions, classic CAN/RTR and X6 ISO FD, with five reopen runs for
each tested mode. These checks used X5 CAN1, X6 CAN1 and X6 CAN2 on one shared
bus; X5 CAN2 was disconnected. X5 was uninitialized for X6 FD tests. This
qualifies neither separate-bus isolation nor an independent FD reference.
Repeated tests verified 1,080 classic/RTR frames and 5,200 FD frames with the
32-bit Python plugin. The target WLAN device was identified by BSSID and UID;
its current IP was supplied explicitly. An X6 WLAN connection failure recovered
after a device power cycle; its underlying cause was not established.

ARM64 was tested on a Firefly ROC-RK3588S-PC with kernel 5.10.160, using an
Ubuntu 24.04 user space in rootless bubblewrap (native execution, no QEMU).
353 installed-plugin tests and matching public/internal ABI probes passed.
Both CANPAL devices used the board's USB controller and Linux CDC ACM driver.
Five reopen runs per mode verified 1,080 classic/RTR frames across dual USB
and both mixed USB/WLAN directions, plus 5,200 X6 FD frames over USB/WLAN.
UID selection also passed after ttyACM numbers swapped through driver rebind;
this was not a physical cable hotplug test. The shared CAN wiring and
independent-reference limits described above also apply to these ARM tests.

**Firefly WLAN condition:** `wlan0` and `p2p0` on this RTL8821CU share one radio
and one channel. Managed-mode tests encountered multi-second TCP pauses during
background scans. X6 repeated acceptance passed with a fixed hotspot BSSID and
channel, background scanning disabled, and power saving off. This qualifies
that configuration only; default managed-mode reliability is still open.
Router and CANPAL hotspot access were tested sequentially, not concurrently.
Other ARM boards, drivers and a complete Ubuntu 24.04 boot image were not tested.

**Not yet qualified:** independent-reference CAN/CAN FD interoperability,
two simultaneous WLAN devices,
full repeated physical renumbering/disconnection acceptance, rated-load/endurance
runs, and native resource stability. Formal 0.1.0 retains the complete release
acceptance requirements.

## Native SDK and firmware requirements

The default runtime is SDK build **663eda8**, bundled with each platform wheel.
No `CANPAL_LIBRARY`, Qt installation or library search-path setting is needed
for the default runtime. USB permissions and a reachable device IP remain OS
configuration requirements.

An explicitly supplied `library_path` takes precedence over `CANPAL_LIBRARY`,
then the bundled SDK, then legacy system lookup. Invalid explicit paths are
errors; they do not silently select a different SDK. External SDK users may
still request build 663eda8 through their existing CANPAL supplier.

The pinned source revision is `663eda823be0533754123695405033b38e115489`. It adds
stricter CAN ACK validation, TCP buffered-read handling, immediate OBD write-error
propagation, UDS source filtering, and synchronized command handoff. It also
includes cooperative worker shutdown and the Qt serial buffered-read fix.
The firmware protocol has no transaction sequence, so a delayed ACK for an
identical earlier request cannot be distinguished by the echoed fields alone.

The following are the original rc2 SDK input hashes. Packaging tools rewrite
dependency names/paths, so final wheel hashes are recorded in each wheel's manifest.

| Runtime input file | SHA256 |
| --- | --- |
| Windows x64 CANPAL.dll | `c5bf185e71d06171302163d47f9c709b791306ecddf911de464d5b432daa943a` |
| Ubuntu x86_64 libCANPAL.so | `a7b5d46e50bdb1fa5a580f59f43498fa78a05b959dd2d0c2bd01f9eba242c70d` |

Matching exported function names or the SDK version string alone does not identify
this fixed build. rc3 adds platform runtime bundles and default library selection.
rc4 adds the Windows 32-bit Python runtime and architecture selection; the CAN
protocol remains unchanged. rc5 adds the Linux ARM64 runtime with the Firefly
conditions above; formal hardware acceptance remains incomplete.

Classic RTR also requires firmware that preserves the received DLC. The original
X5 firmware reporting `0x0332` and X6 firmware reporting `0x0127` cleared RTR DLC
to zero. Supplier-signed diagnostic builds fixed this on the two test devices.
Those builds still report the same version numbers: ask your supplier to identify
the actual fixed build. The plugin cannot recover DLC that firmware has discarded.

For an external SDK only, set `CANPAL_LIBRARY` or pass `library_path`.
On Windows, `CANPAL_DEPENDENCY_DIRS` supplies that external SDK's dependencies;
it is ignored when the bundled SDK is selected. On Linux, external SDK users
must arrange its matching dependencies. USB users need serial-port access
through udev or the dialout group.

The bundled libraries can be replaced under their applicable terms. Hashes
identify release artifacts and are not a runtime replacement lock.

## Connect by UID or IP

```python
import can

with can.Bus(interface="canpal", ignore_config=True,
             transport="usb", device_id="YOUR_24_HEX_DIGIT_UID",
             channel=0, bitrate=500_000) as bus:
    message = bus.recv(timeout=1)
```

The device UID remains the selector when a COM/ttyACM number changes. After a
reconnection, close and recreate the Bus using the same UID; automatic reconnect
is not implemented. Linux USB/IP and physical ARM host driver rebind order swaps have been verified;
the full repeated physical renumbering acceptance remains pending.

Use `transport="wlan", host="192.0.2.10", port=8000` for TCP direct connection.
The IP and port must match the actual device; no network discovery is attempted.
Keep `device_id` to verify identity at that endpoint. X6 CAN FD additionally uses
`fd=True, data_bitrate=2_000_000`.

Use one Bus per CAN channel: `channel=0` selects CAN1 and `channel=1` selects
CAN2. Two channels with the same UID share one native connection; different
devices retain separate connections. Opening the same channel twice is rejected.
Both channels on one device must use the same transport and network endpoint.
Bitrate, FD mode, filters, TX echo, retry attempts and CAN status remain per channel.
Closing one channel leaves its peer running; the last close releases the device.

```python
from contextlib import ExitStack
import can

uids = ["FIRST_24_HEX_DIGIT_UID", "SECOND_24_HEX_DIGIT_UID"]
with ExitStack() as stack:
    buses = {(uid, channel): stack.enter_context(can.Bus(
        interface="canpal", ignore_config=True, transport="usb",
        device_id=uid, channel=channel, bitrate=500_000))
        for uid in uids for channel in (0, 1)}
    # Each buses[(uid, channel)] is an independent python-can Bus.
```

Use one receiver/Notifier per Bus and retain `(device_id, channel)` in application
callbacks: `Message.channel` alone cannot distinguish different devices.
Recv callers share a single native reader and route records in arrival order;
there is no additional background worker. Drain each active channel. Its pending
queue is limited to 4096 records; overflow raises `CanOperationError` on that
channel until it is closed and reopened, without silently dropping records or
faulting the other channel. A device disconnect faults both of its channels;
close both before reconnecting. Other physical devices remain independent.

For several WLAN devices, every endpoint must be reachable from the computer.
Different hotspots using the same IP need explicit network routing; UID checking
prevents selecting the wrong device but does not resolve an IP routing conflict.
UDP and server mode are not implemented.

A successful `send()` means the native queue accepted the frame, not that the
CAN bus acknowledged it. Use a single receiver per Bus, stop its Notifier before
shutdown, and handle python-can exceptions. Timestamp alignment is approximate
per device and does not provide precise synchronization between devices.

Releases distribute platform wheels. Source and build inputs are archived separately;
an incomplete source distribution is not offered as an automatic pip fallback.

## TX echo, retry attempts and diagnostics

TX echo and non-default retry configuration currently require X5 WLAN firmware
0x0333 or X6 WLAN firmware 0x0128 (USB or WLAN connection). Other model/firmware
combinations retain default RX/TX but reject these new options before changing
controller settings. Older FD receipts may lack payload bytes; the SDK can fill
them with zeros, so a higher firmware number alone is not proof of compatibility.

Set `receive_own_messages=True` to request TX results. `recv()` returns a successful
device receipt as `can.Message(is_rx=False)`; incoming traffic has `is_rx=True`.
Both follow normal CAN ID filters. Echoes are not synthesized when `send()` returns.
Missing receipts do not prove failure, and successful transmission does not prove
that another application processed the frame. Delivery follows arrival order;
messages are not sorted by timestamp. TX timestamps mark device completion
processing, not a precise wire timestamp.

`tx_max_attempts` is a Bus-level integer from 1 to 255, default 255, including the
initial attempt. One means no retry, two permits one retry. It applies to ordinary
and periodic sends. The native field also accepts 0 as an alias for one attempt;
the Python option uses 1 explicitly. The earlier native header's n+1 comment was
incorrect for these firmware versions. Retries retain the frame's FIFO position.
Python submits once, and `send(timeout=...)` does not wait for the terminal result.
Bus-off can suspend a frame, so the attempt bound is not a completion deadline.

Subclass `CanpalBus` to handle diagnostics:

```python
from canpal import CanpalBus

class ObservedBus(CanpalBus):
    def handle_canpal_event(self, event):
        # Runs inside recv()/Notifier. Keep this short and nonblocking.
        if event.kind == "tx_failed":
            self.last_failure = event

with ObservedBus(transport="usb", device_id="YOUR_24_HEX_DIGIT_UID",
                 channel=0, bitrate=500_000, receive_own_messages=True,
                 tx_max_attempts=3) as bus:
    message = bus.recv(timeout=1)
    status = bus.last_can_status
```

`CanpalEvent` and `CanStatus` are immutable records from `canpal.events`.
Event kinds are `tx_failed`, `can_error`, `can_status` and `unknown`. Events include
UID, channel, timestamps and a copied SDK record. CAN errors/status bypass CAN ID
filters; ordinary Notifier listeners receive only RX and successful TX messages.
With TX receipts disabled, per-frame terminal failure reporting is not guaranteed.
An intermediate protocol error may be followed by a successful retry.

TX result bits and CAN protocol error numbers use different encodings. X6 can
report nominal/data-phase errors separately; a terminal result may contain only
a general failure even when a separate ACK error event is available. The adapter
does not infer or attach that separate event to a particular transmission.
`attempts` is the firmware's completed attempt count only when its meaning is
known. Queue-full/uninitialized rejections may retain the requested limit and
therefore expose `attempts=None`, with original bytes retained.

`last_can_status` is None until a valid report is received, then contains
initialized/warning/error-passive/bus-off flags, TEC/REC and the observation time.
X5 status does not supply LEC (`last_error=None`); X6 REC is seven bits and its RP
flag is separate. The property performs no I/O and becomes None after detected
link failure or shutdown. It is not a continuously refreshed health query.
`bus.state` is unchanged. Link failures remain python-can exceptions, not CAN
Bus-off events. Unknown firmware layouts remain raw `unknown` events.

There is no additional reader, automatic reconnect, Python retry or receipt
history. Drive `recv()` or a single Notifier to process events. Do not recursively
receive or join the current Notifier from the hook. Hook exceptions propagate;
they do not mark the connection offline. Stop Notifier before shutdown.
