Files
elmer/obd/commands.py
“Naeel” aa37a1a70c v0.48.0 — пробинг ELM327, трехуровневый профиль, рефакторинг obd/
- obd/probe.py: трехуровневый каскад (L0/L1/L2)
- obd/commands.py: каталог всех AT-команд с метаданными
- obd/classifier.py: классификация ответов + определение уровня
- obd/connection.py: транспортный слой (SerialTransport)
- obd/protocol.py: init() только база, без ATAT1/ATST
- api/db.py: таблица device_profiles по BT MAC
- api/scripts.py: три уровня скриптов (l0/l1/l2)
- api/routes.py: /elm/probe, /elm/profile/<mac>, /script?level=
- web/templates/index.html: v0.48.0
- CHANGELOG.md, doc/architecture.md, resume.txt: версии
2026-06-07 05:55:02 +04:00

144 lines
6.8 KiB
Python

"""
obd/commands.py — Каталог AT-команд ELM327.
Все известные команды ELM327 с метаданными:
- name: имя команды (напр. "ATE0")
- desc: краткое описание
- level: минимальный уровень устройства (0/1/2)
- safe: можно ли слать при пробинге (False для опасных)
Принцип:
- Нет в профиле → не слать.
- Опасные команды (меняют baud rate, мониторинг) — никогда не пробингуются.
"""
from dataclasses import dataclass
from typing import List
@dataclass
class AtCommand:
"""AT-команда ELM327 с метаданными."""
name: str # "ATE0"
desc: str # "Echo off"
level: int = 0 # минимальный уровень (0=все, 1=ATAT1, 2=CAF1/CFC1)
safe: bool = True # можно ли пробинговать
# ══════════════════════════════════════════════════════════
# Уровень 0 — Универсальные (есть у ВСЕХ клонов)
# ══════════════════════════════════════════════════════════
CMDS_L0: List[AtCommand] = [
AtCommand("ATE0", "Echo off"),
AtCommand("ATE1", "Echo on", safe=False), # не слать — вернём эхо
AtCommand("ATL0", "Linefeeds off"),
AtCommand("ATL1", "Linefeeds on", safe=False),
AtCommand("ATS0", "Spaces off"),
AtCommand("ATS1", "Spaces on", safe=False),
AtCommand("ATH0", "Headers off"),
AtCommand("ATH1", "Headers on"),
AtCommand("ATSP0", "Auto protocol"),
AtCommand("ATDPN", "Describe protocol number"),
AtCommand("ATRV", "Read voltage"),
AtCommand("ATI", "Identify"),
AtCommand("ATWS", "Warm start", safe=False), # сбрасывает состояние
AtCommand("ATPC", "Protocol close", safe=False),
AtCommand("ATR0", "Responses off", safe=False),
AtCommand("ATR1", "Responses on"),
AtCommand("ATNL", "Normalize length"),
AtCommand("ATAL", "Allow long messages"),
AtCommand("ATAR", "Auto receive", safe=False), # включает авто-приём
AtCommand("ATSW", "Stop waiting"),
AtCommand("ATFE", "Forget events"),
AtCommand("ATSI", "Slow init"),
AtCommand("ATFI", "Fast init"),
AtCommand("ATKW", "Keyword"),
AtCommand("ATTA", "Tester address"),
AtCommand("ATTP", "Try protocol"),
AtCommand("ATWM", "Wakeup message"),
AtCommand("ATIGN", "Ignore"),
AtCommand("ATIFR", "IFR"),
AtCommand("ATIIA", "ISO init address"),
AtCommand("ATRD", "Read data"),
AtCommand("ATCS", "CAN silent"),
AtCommand("ATCV", "Calibrate voltage"),
AtCommand("ATD0", "Display off"),
AtCommand("ATM0", "Memory off"),
AtCommand("ATM1", "Memory on"),
]
# ══════════════════════════════════════════════════════════
# Уровень 1 — Хорошие клоны и оригиналы
# ══════════════════════════════════════════════════════════
CMDS_L1: List[AtCommand] = [
AtCommand("ATAT0", "Adaptive timing off", level=1),
AtCommand("ATAT1", "Adaptive timing on", level=1),
AtCommand("ATAT2", "Adaptive timing aggressive", level=1),
]
# ══════════════════════════════════════════════════════════
# Уровень 2 — Только настоящий ELM327
# ══════════════════════════════════════════════════════════
CMDS_L2: List[AtCommand] = [
AtCommand("ATCAF0", "CAN auto formatting off", level=2),
AtCommand("ATCAF1", "CAN auto formatting on", level=2),
AtCommand("ATCFC0", "CAN flow control off", level=2),
AtCommand("ATCFC1", "CAN flow control on", level=2),
AtCommand("ATCM", "CAN mask", level=2),
AtCommand("ATCRA", "CAN receive address", level=2),
AtCommand("ATCP", "CAN priority", level=2),
AtCommand("ATSH", "Set header", level=2),
AtCommand("AT@1", "Device description", level=2),
AtCommand("AT@2", "Device identifier", level=2),
AtCommand("ATJE", "J1939 ELM", level=2),
AtCommand("ATJS", "J1939 setup", level=2),
AtCommand("ATIB", "ISO baud", level=2, safe=False),
]
# ══════════════════════════════════════════════════════════
# ОПАСНЫЕ — никогда не пробинговать
# ══════════════════════════════════════════════════════════
CMDS_DANGEROUS: List[AtCommand] = [
AtCommand("ATZ", "Hard reset", safe=False),
AtCommand("ATD", "Set defaults", safe=False),
AtCommand("ATBD", "Baud rate", safe=False),
AtCommand("ATBRD", "Baud rate divisor", safe=False),
AtCommand("ATMA", "Monitor all", safe=False),
AtCommand("ATMR", "Monitor receiver", safe=False),
AtCommand("ATMT", "Monitor transmitter", safe=False),
]
# ── Хелперы ─────────────────────────────────────────────
def get_commands_for_level(level: int) -> List[str]:
"""Возвращает имена команд, доступных на заданном уровне."""
result = [c.name for c in CMDS_L0 if c.safe]
if level >= 1:
result += [c.name for c in CMDS_L1 if c.safe]
if level >= 2:
result += [c.name for c in CMDS_L2 if c.safe]
return result
def get_probe_commands() -> dict:
"""Возвращает команды для пробинга, сгруппированные по уровням.
Returns: {"L0": ["ATE0",...], "L1": ["ATAT1",...], "L2": ["ATCAF1",...]}
"""
return {
"L0": [c.name for c in CMDS_L0 if c.safe and c.level == 0][:8],
"L1": [c.name for c in CMDS_L1 if c.safe],
"L2": [c.name for c in CMDS_L2 if c.safe],
}
# Плоские списки для импорта (обратная совместимость)
L0_NAMES = [c.name for c in CMDS_L0 if c.safe and c.level == 0]
L1_NAMES = [c.name for c in CMDS_L1 if c.safe]
L2_NAMES = [c.name for c in CMDS_L2 if c.safe]