mirror of
https://github.com/controllerzz/carbus_lib.git
synced 2026-07-15 16:30:16 +03:00
update 0.1.9
This commit is contained in:
@@ -1,27 +1,67 @@
|
||||
# carbus-lib (async CAN / ISO-TP / UDS stack)
|
||||
# carbus-lib
|
||||
|
||||
Асинхронная библиотека на Python для работы с CAN-адаптером **CAN-Hacker / Car Bus Analyzer**:
|
||||
Асинхронная Python-библиотека для работы с CAN / CAN-FD адаптерами
|
||||
**CAN-Hacker / Car Bus Analyzer**: низкоуровневый CAN/LIN, ISO-TP, UDS и удалённый
|
||||
доступ к адаптеру через сеть.
|
||||
|
||||
- 📡 **`carbus_async`** – низкоуровневая работа с железкой (CAN/LIN, фильтры, терминаторы и т.д.)
|
||||
- 📦 **`isotp_async`** – ISO-TP (ISO 15765-2) поверх CAN (single + multi-frame)
|
||||
- 🩺 **`uds_async`** – UDS (ISO 14229) клиент и сервер (диагностика, чтение VIN и т.п.)
|
||||
- 🌐 **`Remote/TCP-bridge`** – удалённое подключение к адаптеру через сеть (как будто он воткнут локально)
|
||||
- 📡 **`carbus_async`** — работа с железом: CAN/CAN-FD, фильтры, терминатор 120 Ω, периодическая отправка, хуки
|
||||
- 📦 **`isotp_async`** — ISO-TP (ISO 15765-2): single- и multi-frame
|
||||
- 🩺 **`uds_async`** — UDS (ISO 14229): клиент и сервер (эмуляция ЭБУ)
|
||||
- 🌐 **Remote / TCP-bridge** — удалённый доступ к адаптеру через сеть, как к локальному
|
||||
|
||||
> Python 3.10 и выше
|
||||
> Никаких «магических» зависимостей — всё на `asyncio`.
|
||||
> Поддерживаемые интерфейсы: https://canhacker.ru/shop/
|
||||
> _*Тестировалось на устройствах с Протоколом Версии 22_
|
||||
> Python ≥ 3.10 · зависимости: `pyserial`, `pyserial-asyncio` · целиком на `asyncio`
|
||||
> Протестировано на устройствах с **Протоколом версии 22**
|
||||
> Поддерживаемые устройства: https://canhacker.ru/shop/
|
||||
|
||||
---
|
||||
|
||||
## Содержание
|
||||
|
||||
- [Возможности](#возможности)
|
||||
- [Установка](#установка)
|
||||
- [Быстрый старт](#быстрый-старт)
|
||||
- [CAN-FD и BRS](#can-fd-и-brs)
|
||||
- [Приём и хуки](#приём-и-хуки)
|
||||
- [Периодическая отправка](#периодическая-отправка)
|
||||
- [Фильтры](#фильтры)
|
||||
- [Терминатор](#терминатор)
|
||||
- [Информация об устройстве](#информация-об-устройстве)
|
||||
- [Маршрутизация по CAN-ID](#маршрутизация-по-can-id)
|
||||
- [ISO-TP](#iso-tp)
|
||||
- [UDS клиент](#uds-клиент)
|
||||
- [UDS сервер](#uds-сервер)
|
||||
- [Удалённая работа](#удалённая-работа)
|
||||
- [Логирование](#логирование)
|
||||
- [Примеры](#примеры)
|
||||
- [Лицензия](#лицензия)
|
||||
|
||||
---
|
||||
|
||||
## Возможности
|
||||
|
||||
- CAN / CAN-FD: отправка и приём, BRS (bit-rate switching)
|
||||
- Настройка каналов по битрейту или по точным bit-timing
|
||||
- Фильтры по 11- и 29-битным ID, очистка фильтров
|
||||
- Управление встроенным терминатором 120 Ω
|
||||
- Периодическая отправка кадров (статичные данные или с модификацией на лету)
|
||||
- Хуки на приём кадров по ID и по маске данных
|
||||
- Маршрутизация принятых кадров по CAN-ID в отдельные очереди
|
||||
- ISO-TP (single + multi-frame)
|
||||
- UDS клиент и UDS сервер (эмуляция ЭБУ)
|
||||
- Удалённая работа через relay-сервер и TCP-мост
|
||||
- Подробное логирование всего протокольного трафика
|
||||
|
||||
---
|
||||
|
||||
## Установка
|
||||
|
||||
Через систему управления пакетами PIP
|
||||
````bash
|
||||
python -m pip install carbus-lib
|
||||
````
|
||||
Из PyPI:
|
||||
|
||||
Либо как editable-модуль из репозитория:
|
||||
```bash
|
||||
python -m pip install carbus-lib
|
||||
```
|
||||
|
||||
Либо editable-режим из репозитория (нужно для разработки):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/controllerzz/carbus_lib.git
|
||||
@@ -29,142 +69,137 @@ cd carbus_lib
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
# carbus-lib
|
||||
|
||||
Асинхронная библиотека для работы с CAN / CAN-FD, ISO-TP и UDS.
|
||||
Поддерживает локальное подключение через USB CDC и удалённую работу через TCP-bridge.
|
||||
|
||||
---
|
||||
|
||||
## Возможности
|
||||
## Быстрый старт
|
||||
|
||||
- CAN / CAN-FD отправка и приём
|
||||
- Настройка каналов, скоростей, режимов, BRS
|
||||
- Фильтры ID, очистка фильтров, управление терминатором 120 Ω
|
||||
- ISO-TP (single + multi-frame)
|
||||
- UDS Client и UDS Server (эмуляция ЭБУ)
|
||||
- TCP-мост: удалённая работа с адаптером так, как будто он подключён локально
|
||||
- Логирование всего протокольного трафика
|
||||
Открыть устройство, настроить классический CAN-канал, отправить и принять кадр.
|
||||
|
||||
---
|
||||
|
||||
## Работа с CAN
|
||||
|
||||
Простейший пример: открыть устройство, настроить канал и отправить / принять кадр.
|
||||
|
||||
````python
|
||||
```python
|
||||
import asyncio
|
||||
from carbus_async.device import CarBusDevice
|
||||
from carbus_async.messages import CanMessage
|
||||
from carbus_async import CarBusDevice, CanMessage
|
||||
|
||||
|
||||
async def main():
|
||||
dev = await CarBusDevice.open("COM6", baudrate=115200)
|
||||
|
||||
# классический CAN 500 kbit/s
|
||||
await dev.open_can_channel(
|
||||
channel=1,
|
||||
nominal_bitrate=500_000,
|
||||
)
|
||||
# классический CAN, 500 kbit/s, канал 1
|
||||
await dev.open_can_channel(channel=1, nominal_bitrate=500_000)
|
||||
|
||||
# включаем терминатор 120 Ω на канале 1
|
||||
await dev.set_terminator(channel=1, enabled=True)
|
||||
# внутренний терминатор 120 Ω (если поддерживается устройством)
|
||||
await dev.ensure_terminator(channel=1, enabled=True)
|
||||
|
||||
# отправка кадра 0x7E0 8 байт
|
||||
# отправка кадра
|
||||
msg = CanMessage(can_id=0x7E0, data=b"\x02\x3E\x00\x00\x00\x00\x00\x00")
|
||||
await dev.send_can(msg, channel=1)
|
||||
|
||||
# приём любого сообщения
|
||||
# приём любого кадра -> (channel, CanMessage)
|
||||
ch, rx = await dev.receive_can()
|
||||
print("RX:", ch, rx)
|
||||
print("RX:", ch, hex(rx.can_id), rx.data.hex())
|
||||
|
||||
await dev.close()
|
||||
|
||||
asyncio.run(main())
|
||||
````
|
||||
|
||||
## Настройка канала через Bit Timing
|
||||
Возможность конфигруации скорости CAN канала через Bit Timing
|
||||
````python
|
||||
# CANFD+BRS 500/2000 kbit/s
|
||||
await dev.open_can_channel_custom(
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
Классический CAN через `open_can_channel` поддерживает битрейты
|
||||
10k / 20k / 33.3k / 50k / 62.5k / 83.3k / 95.2k / 100k / 125k / 250k / 400k / 500k / 800k / 1M.
|
||||
|
||||
---
|
||||
|
||||
## CAN-FD и BRS
|
||||
|
||||
### 1. По стандартным битрейтам
|
||||
|
||||
```python
|
||||
await dev.open_can_channel(
|
||||
channel=1,
|
||||
nominal_timing=CanTiming(
|
||||
prescaler=15,
|
||||
tq_seg1=12,
|
||||
tq_seg2=3,
|
||||
sjw=1
|
||||
),
|
||||
data_timing=CanTiming(
|
||||
prescaler=6,
|
||||
tq_seg1=7,
|
||||
tq_seg2=2,
|
||||
sjw=1
|
||||
),
|
||||
nominal_bitrate=500_000, # арбитражная фаза
|
||||
data_bitrate=2_000_000, # фаза данных (используется при BRS)
|
||||
fd=True,
|
||||
brs=True,
|
||||
)
|
||||
````
|
||||
```
|
||||
|
||||
## Получение информации об устройстве:
|
||||
Получение инфмормации об устройстве и его фичах
|
||||
````python
|
||||
info = await dev.get_device_info()
|
||||
Поддерживаемые битрейты (CAN-модуль 120 МГц, точка выборки ~80%):
|
||||
|
||||
print("HW:", info.hardware_name)
|
||||
print("FW:", info.firmware_version)
|
||||
print("Serial:", info.serial_int)
|
||||
- **nominal:** 125k / 250k / 500k / 1M
|
||||
- **data:** 500k / 1M / 2M / 4M / 5M
|
||||
|
||||
print("Features:",
|
||||
"gateway" if info.feature_gateway else "",
|
||||
"isotp" if info.feature_isotp else "",
|
||||
"txbuf" if info.feature_tx_buffer else "",
|
||||
"txtask" if info.feature_tx_task else "",
|
||||
)
|
||||
````
|
||||
> На этих адаптерах FD-канал настраивается точными bit-timing, а не одиночным
|
||||
> индексом скорости данных — иначе канал открывается, но FD/BRS-кадры не уходят
|
||||
> в шину. `open_can_channel` делает это автоматически. Если тактовая модуля не
|
||||
> 120 МГц, передайте её через `can_clock_hz=...`.
|
||||
|
||||
## Пример настройки фильтров:
|
||||
11 bit фильтры имеют index от 0 до 27 включительно,
|
||||
29 bit фильтры имеют index от 28 до 35 включительно
|
||||
````python
|
||||
# очистить все фильтры на канале 1
|
||||
await dev.clear_all_filters(1)
|
||||
### 2. По точным bit-timing
|
||||
|
||||
# разрешить только ответы с ID 0x7E8 (11-битный стандартный ID)
|
||||
await dev.set_std_id_filter(
|
||||
Для нестандартных скоростей задайте тайминги напрямую:
|
||||
|
||||
```python
|
||||
from carbus_async import CanTiming
|
||||
|
||||
await dev.open_can_channel_custom(
|
||||
channel=1,
|
||||
index=0,
|
||||
can_id=0x7E8,
|
||||
mask=0x7FF,
|
||||
nominal_timing=CanTiming(prescaler=15, tq_seg1=12, tq_seg2=3, sjw=1), # 500k @ 120 МГц
|
||||
data_timing=CanTiming(prescaler=6, tq_seg1=7, tq_seg2=2, sjw=1), # 2M @ 120 МГц
|
||||
fd=True,
|
||||
brs=True,
|
||||
)
|
||||
````
|
||||
```
|
||||
|
||||
## Управление терминатором 120 Ω:
|
||||
Включаем терминатор на канале 1 и выключаем терминатор на канале 2
|
||||
````python
|
||||
await dev.set_terminator(channel=1, enabled=True)
|
||||
await dev.set_terminator(channel=2, enabled=False)
|
||||
````
|
||||
### Отправка FD-кадра
|
||||
|
||||
Проверка наличия внутреннего терминатора у девайса
|
||||
````python
|
||||
if await dev.has_terminator():
|
||||
await dev.set_terminator(channel=1, enabled=True)
|
||||
print("Device has an internal terminator")
|
||||
else:
|
||||
print("Device does not have an internal terminator")
|
||||
````
|
||||
Тип кадра задаётся флагами `fd` / `brs` в самом `CanMessage`:
|
||||
|
||||
Включаем терминатор на канале 1, если это поддерживает девайс
|
||||
````python
|
||||
await dev.ensure_terminator(channel=1, enabled=True)
|
||||
````
|
||||
```python
|
||||
# FD-кадр с переключением скорости (BRS), 16 байт данных
|
||||
msg = CanMessage(can_id=0x100, data=bytes(range(16)), fd=True, brs=True)
|
||||
await dev.send_can(msg, channel=1)
|
||||
```
|
||||
|
||||
## Отправка периодичных сообщений:
|
||||
Отправка сообщений с статичными данными и периодом 100мс
|
||||
````python
|
||||
> Допустимые длины данных CAN-FD: 0–8, 12, 16, 20, 24, 32, 48, 64 байт.
|
||||
|
||||
---
|
||||
|
||||
## Приём и хуки
|
||||
|
||||
Способы приёма:
|
||||
|
||||
```python
|
||||
ch, msg = await dev.receive_can() # с любого канала
|
||||
msg = await dev.receive_can_on(channel=1) # только канал 1
|
||||
msg = await dev.receive_can_on_timeout(channel=1, timeout=1.0) # None при таймауте
|
||||
```
|
||||
|
||||
Хуки (вызываются на каждый подходящий принятый кадр):
|
||||
|
||||
```python
|
||||
# по CAN-ID
|
||||
@dev.on_can_id(0x7E0)
|
||||
async def on_engine(ch, msg):
|
||||
print("ENGINE:", hex(msg.can_id), msg.data.hex())
|
||||
|
||||
# по CAN-ID + совпадению данных по маске
|
||||
@dev.on_can_match(
|
||||
can_id=0x7E0,
|
||||
value=b"\x02\x10\x00",
|
||||
mask=b"\xFF\xFF\x00",
|
||||
)
|
||||
async def on_session_control(ch, msg):
|
||||
print("DiagnosticSessionControl")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Периодическая отправка
|
||||
|
||||
```python
|
||||
from carbus_async import PeriodicCanSender
|
||||
|
||||
sender = PeriodicCanSender(dev)
|
||||
|
||||
# статичные данные, период 100 мс (классический CAN)
|
||||
sender.add(
|
||||
"heartbeat",
|
||||
channel=1,
|
||||
@@ -172,14 +207,8 @@ sender.add(
|
||||
data=b"\x01\x02\x03\x04\x05\x06\x07\x08",
|
||||
period_s=0.1,
|
||||
)
|
||||
````
|
||||
|
||||
Отправка сообщений с модификацией данных и периодом 500мс
|
||||
````python
|
||||
from carbus_async import PeriodicCanSender
|
||||
|
||||
sender = PeriodicCanSender(dev)
|
||||
|
||||
# CAN-FD + BRS со счётчиком в первом байте
|
||||
def mod(tick, data):
|
||||
b = bytearray(data)
|
||||
b[0] = tick & 0xFF
|
||||
@@ -191,160 +220,280 @@ sender.add(
|
||||
can_id=0x100,
|
||||
data=b"\x00" * 8,
|
||||
period_s=0.5,
|
||||
modify=mod)
|
||||
````
|
||||
|
||||
|
||||
## Хуки подписка на сообщение / сообщение + данные по маске:
|
||||
Подписка по CAN ID
|
||||
````python
|
||||
@dev.on_can_id(0x7E0)
|
||||
async def on_engine_req(ch, msg):
|
||||
print("ENGINE:", hex(msg.can_id), msg.data.hex())
|
||||
````
|
||||
|
||||
Подписка по CAN ID + маске данных
|
||||
````python
|
||||
@dev.on_can_match(
|
||||
can_id=0x7E0,
|
||||
value=b"\x02\x10\x00",
|
||||
mask=b"\xFF\xFF\x00",
|
||||
fd=True,
|
||||
brs=True,
|
||||
modify=mod,
|
||||
)
|
||||
async def on_session_control(ch, msg):
|
||||
print("SessionControl")
|
||||
````
|
||||
|
||||
## ISO-TP (isotp_async)
|
||||
ISO-TP канал строится поверх CarBusDevice:
|
||||
````python
|
||||
|
||||
# управление задачами
|
||||
await sender.remove("cnt") # остановить и удалить одну задачу
|
||||
await sender.stop_all() # остановить все
|
||||
```
|
||||
|
||||
Параметры `add(...)`: `channel`, `can_id`, `data`, `period_s`,
|
||||
`modify` (функция `(tick, data) -> bytes`, может быть async), `extended`,
|
||||
`fd`, `brs`, `rtr`, `echo`, `confirm`, `autostart`.
|
||||
|
||||
---
|
||||
|
||||
## Фильтры
|
||||
|
||||
11-битные фильтры занимают индексы `0..27`, 29-битные — `28..35`.
|
||||
|
||||
```python
|
||||
# очистить все фильтры на канале 1
|
||||
await dev.clear_all_filters(1)
|
||||
|
||||
# пропускать только ответы с ID 0x7E8 (11-бит)
|
||||
await dev.set_std_id_filter(channel=1, index=0, can_id=0x7E8, mask=0x7FF)
|
||||
|
||||
# 29-битный фильтр
|
||||
await dev.set_ext_id_filter(channel=1, index=28, can_id=0x18DAF110)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Терминатор
|
||||
|
||||
```python
|
||||
await dev.set_terminator(channel=1, enabled=True)
|
||||
await dev.set_terminator(channel=2, enabled=False)
|
||||
|
||||
# проверка поддержки и включение
|
||||
if await dev.has_terminator(channel=1):
|
||||
await dev.set_terminator(channel=1, enabled=True)
|
||||
|
||||
# включить, только если устройство это поддерживает
|
||||
await dev.ensure_terminator(channel=1, enabled=True)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Информация об устройстве
|
||||
|
||||
```python
|
||||
info = await dev.get_device_info()
|
||||
|
||||
print("HW:", info.hardware_name)
|
||||
print("FW:", info.firmware_version)
|
||||
print("Serial:", info.serial_int)
|
||||
print("Каналы:", info.channel_types) # {1: 'CANFD', 2: 'CAN', ...}
|
||||
print("Частоты:", info.channel_frequencies) # {1: 120000000, ...} Гц
|
||||
|
||||
print("Фичи:",
|
||||
"gateway" if info.feature_gateway else "",
|
||||
"isotp" if info.feature_isotp else "",
|
||||
"txbuf" if info.feature_tx_buffer else "",
|
||||
"txtask" if info.feature_tx_task else "")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Маршрутизация по CAN-ID
|
||||
|
||||
`CanIdRouter` раскладывает принятые кадры по отдельным очередям на каждый CAN-ID —
|
||||
удобно, когда на одном канале несколько независимых потоков.
|
||||
|
||||
```python
|
||||
from carbus_async import CanIdRouter
|
||||
|
||||
router = CanIdRouter(dev, channel=1)
|
||||
await router.start()
|
||||
|
||||
q = router.get_queue(0x7E8) # очередь только для кадров с ID 0x7E8
|
||||
msg = await q.get()
|
||||
print(msg.data.hex())
|
||||
|
||||
await router.stop()
|
||||
```
|
||||
|
||||
Роутер можно передать в `open_isotp(..., router=router)`, чтобы держать несколько
|
||||
ISO-TP соединений на одном канале, не «перехватывая» кадры друг друга.
|
||||
|
||||
---
|
||||
|
||||
## ISO-TP
|
||||
|
||||
ISO-TP-канал строится поверх `CarBusDevice`:
|
||||
|
||||
```python
|
||||
from isotp_async import open_isotp
|
||||
|
||||
isotp = await open_isotp(dev, channel=1, tx_id=0x7E0, rx_id=0x7E8)
|
||||
|
||||
# отправить запрос ReadDataByIdentifier F190 (VIN)
|
||||
# ReadDataByIdentifier F190 (VIN)
|
||||
await isotp.send_pdu(b"\x22\xF1\x90")
|
||||
|
||||
# получить полный ответ (single или multi-frame)
|
||||
# полный ответ (single или multi-frame)
|
||||
resp = await isotp.recv_pdu(timeout=5.0)
|
||||
print("ISO-TP:", resp.hex())
|
||||
````
|
||||
```
|
||||
|
||||
## UDS Client (uds_async.client)
|
||||
---
|
||||
|
||||
Клиент UDS использует IsoTpChannel:
|
||||
````python
|
||||
## UDS клиент
|
||||
|
||||
```python
|
||||
from isotp_async import open_isotp
|
||||
from uds_async import UdsClient
|
||||
|
||||
isotp = await open_isotp(dev, channel=1, tx_id=0x7E0, rx_id=0x7E8)
|
||||
uds = UdsClient(isotp)
|
||||
|
||||
await uds.diagnostic_session_control(0x03) # extended session
|
||||
vin = await uds.read_data_by_identifier(0xF190)
|
||||
print("VIN:", vin.decode(errors="ignore"))
|
||||
````
|
||||
await uds.tester_present()
|
||||
```
|
||||
|
||||
## Удалённая работа через внешний сервер
|
||||
### 0. Бесплатный Сервер/Relay
|
||||
Можно воспользоваться бесплатным сервером:
|
||||
Доступные сервисы: `diagnostic_session_control`, `read_data_by_identifier`,
|
||||
`write_data_by_identifier`, `security_access_get_seed`,
|
||||
`security_access_send_key`, `tester_present`.
|
||||
|
||||
IP: 185.42.26.80
|
||||
PORT: 9000
|
||||
---
|
||||
|
||||
### 1. Сервер/Relay
|
||||
На сервере устанавливаем библиотеку carbus-lib, далее запускаем сервер
|
||||
## UDS сервер
|
||||
|
||||
carbus-relay-server --host 0.0.0.0 --port 9000
|
||||
или
|
||||
Эмуляция ЭБУ: на каждый сервис (SID) вешается обработчик, возвращающий полный
|
||||
ответный PDU. Отрицательный ответ — через `UdsNegativeResponse`.
|
||||
|
||||
python -m carbus_async.remote.server --host 0.0.0.0 --port 9000
|
||||
```python
|
||||
from isotp_async import open_isotp
|
||||
from uds_async import UdsServer
|
||||
from uds_async.exceptions import UdsNegativeResponse
|
||||
|
||||
### 2. Агент
|
||||
На машине куда подключен девайс запускаем агента
|
||||
# слушаем запросы на 0x7E0, отвечаем на 0x7E8
|
||||
isotp = await open_isotp(dev, channel=1, tx_id=0x7E8, rx_id=0x7E0)
|
||||
server = UdsServer(isotp)
|
||||
|
||||
carbus-relay-agent --port COM6 --baudrate 115200 --server <IP_СЕРВЕРА>:9000 --serial 5957 --password 1234
|
||||
или
|
||||
|
||||
python -m carbus_async.remote.agent --port COM6 --baudrate 115200 --server <IP_СЕРВЕРА>:9000 --serial 5957 --password 1234
|
||||
@server.service(0x22) # ReadDataByIdentifier
|
||||
async def on_rdbi(req: bytes) -> bytes:
|
||||
did = (req[1] << 8) | req[2]
|
||||
if did == 0xF190:
|
||||
return b"\x62\xF1\x90" + b"WVWZZZ1KZAW000001"
|
||||
raise UdsNegativeResponse(req_sid=0x22, nrc=0x31) # requestOutOfRange
|
||||
|
||||
где
|
||||
await server.serve_forever()
|
||||
```
|
||||
|
||||
--server <IP_СЕРВЕРА>:9000 - адрес и порт сервера
|
||||
--serial 5957 - серийный номер девайса
|
||||
--password 1234 - пароль, требуется для получения уделнного доступа
|
||||
---
|
||||
|
||||
### 3. Клиент (удалённая машина)
|
||||
Для получения удаленного доступа используем функцию ***open_remote_device***
|
||||
## Удалённая работа
|
||||
|
||||
````python
|
||||
### Через relay-сервер (любой IP)
|
||||
|
||||
Можно воспользоваться бесплатным сервером:
|
||||
|
||||
```
|
||||
IP: 185.42.26.80
|
||||
PORT: 9000
|
||||
```
|
||||
|
||||
**Сервер / Relay** (машина с белым IP):
|
||||
|
||||
```bash
|
||||
carbus-relay-server --host 0.0.0.0 --port 9000
|
||||
# или
|
||||
python -m carbus_async.remote.server --host 0.0.0.0 --port 9000
|
||||
```
|
||||
|
||||
**Агент** (машина, к которой подключён адаптер):
|
||||
|
||||
```bash
|
||||
carbus-relay-agent --port COM6 --baudrate 115200 --server <IP_СЕРВЕРА>:9000 --serial 5957 --password 1234
|
||||
# или
|
||||
python -m carbus_async.remote.agent --port COM6 --baudrate 115200 --server <IP_СЕРВЕРА>:9000 --serial 5957 --password 1234
|
||||
```
|
||||
|
||||
| параметр | назначение |
|
||||
|---|---|
|
||||
| `--server <IP>:9000` | адрес и порт relay-сервера |
|
||||
| `--serial 5957` | серийный номер устройства |
|
||||
| `--password 1234` | пароль для удалённого доступа |
|
||||
|
||||
**Клиент** (удалённая машина):
|
||||
|
||||
```python
|
||||
from carbus_async import open_remote_device
|
||||
dev = await open_remote_device(<IP_СЕРВЕРА>, 9000, serial=5957, password="1234")
|
||||
````
|
||||
|
||||
## Удалённая работа в локальной сети через TCP (tcp_bridge)
|
||||
dev = await open_remote_device("185.42.26.80", 9000, serial=5957, password="1234")
|
||||
# дальше — обычный API CarBusDevice
|
||||
```
|
||||
|
||||
### 1. Сервер (рядом с адаптером)
|
||||
### Через TCP-мост в локальной сети
|
||||
|
||||
На машине, где физически подключён CAN-адаптер:
|
||||
**Сервер** (рядом с адаптером):
|
||||
|
||||
python.exe -m carbus_async.tcp_bridge --serial COM6 --port 7000
|
||||
```bash
|
||||
python -m carbus_async.tcp_bridge --serial COM6 --port 7000
|
||||
```
|
||||
|
||||
Адаптер открывается локально, а поверх него поднимается TCP-мост.
|
||||
**Клиент** (та же сеть) — тот же API, но через `open_tcp`:
|
||||
|
||||
### 2. Клиент (удалённая машина)
|
||||
|
||||
На другой машине можно использовать тот же API, как с локальным COM, но через `open_tcp`:
|
||||
````python
|
||||
```python
|
||||
import asyncio
|
||||
from carbus_async.device import CarBusDevice
|
||||
from carbus_async.messages import CanMessage
|
||||
from carbus_async import CarBusDevice, CanMessage
|
||||
|
||||
|
||||
async def main():
|
||||
dev = await CarBusDevice.open_tcp("192.168.1.10", 7000)
|
||||
|
||||
await dev.open_can_channel(
|
||||
channel=1,
|
||||
nominal_bitrate=500_000,
|
||||
fd=False,
|
||||
)
|
||||
await dev.open_can_channel(channel=1, nominal_bitrate=500_000)
|
||||
|
||||
msg = CanMessage(can_id=0x321, data=b"\x01\x02\x03\x04")
|
||||
await dev.send_can(msg, channel=1)
|
||||
|
||||
ch, rx = await dev.receive_can()
|
||||
print("REMOTE RX:", ch, rx)
|
||||
print("REMOTE RX:", ch, hex(rx.can_id), rx.data.hex())
|
||||
|
||||
await dev.close()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
````
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Логирование
|
||||
|
||||
Для отладки удобно включить подробное логирование:
|
||||
````python
|
||||
```python
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||
)
|
||||
````
|
||||
```
|
||||
|
||||
Логгеры:
|
||||
|
||||
- `carbus_async.wire.*` — сырые кадры по USB/TCP (TX/RX)
|
||||
- `carbus_async.device.*` — высокоуровневые события, ошибки, BUS_ERROR
|
||||
- дополнительные логгеры в isotp_async / uds_async
|
||||
- `carbus_async.device.*` — высокоуровневые события, ошибки, `BUS_ERROR`
|
||||
- дополнительные логгеры в `isotp_async` / `uds_async`
|
||||
|
||||
---
|
||||
|
||||
## Примеры
|
||||
|
||||
Готовые скрипты — в каталоге [`example/`](example):
|
||||
|
||||
| файл | что показывает |
|
||||
|---|---|
|
||||
| `can_periodic_message.py` | периодическая отправка (классический CAN) |
|
||||
| `canfd_periodic_message.py` | периодическая отправка CAN-FD / BRS |
|
||||
| `can_message_hook.py` | хуки на приём кадров |
|
||||
| `uds_service22_scaner.py` | скан UDS-сервиса 0x22 (RDBI) |
|
||||
| `uds_id_scaner.py` | перебор идентификаторов (DID) |
|
||||
| `uds_ecu_emulated.py` | эмуляция ЭБУ (UDS-сервер) |
|
||||
| `car_ecus_emulated.py` | эмуляция нескольких ЭБУ |
|
||||
| `remote_can_periodic_message.py` | периодика через relay-сервер |
|
||||
| `remote_can_bridge.py` | мост CAN ↔ удалённое устройство |
|
||||
| `remote_relay.py` | запуск relay-сервера |
|
||||
|
||||
---
|
||||
|
||||
## Лицензия
|
||||
|
||||
MIT.
|
||||
|
||||
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
|
||||
|
||||
|
||||
MIT — распространяется «как есть», без каких-либо гарантий. См. [LICENSE](LICENSE).
|
||||
|
||||
Pull Requests и предложения по улучшению приветствуются 🚗
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user