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: версии
This commit is contained in:
+77
-3
@@ -1,8 +1,9 @@
|
||||
"""Эндпоинты: скрипт, загрузка сессии, чат.
|
||||
"""Эндпоинты: скрипт, загрузка сессии, чат, пробинг ELM.
|
||||
|
||||
GET /api/v1/script — выдача скрипта диагностики
|
||||
POST /api/v1/session/upload — приём батча + LLM
|
||||
POST /api/v1/chat — свободный вопрос к LLM
|
||||
POST /api/v1/elm/probe — пробинг ELM327, определение уровня
|
||||
|
||||
См. также: api/dtc.py (DTC), api/ping.py (ping)
|
||||
"""
|
||||
@@ -15,7 +16,7 @@ from flask import jsonify, request
|
||||
from api.config import load
|
||||
from api.db import Database
|
||||
from api.parser import format_no_llm, parse_batch
|
||||
from api.scripts import build_default_script, build_full_script
|
||||
from api.scripts import build_default_script, build_full_script, build_script_for_level
|
||||
from brain.client import Diagnoser, LLMError
|
||||
from brain.prompts import SYSTEM_PROMPT
|
||||
|
||||
@@ -70,7 +71,16 @@ def register(app):
|
||||
@app.route("/api/v1/script", methods=["GET"])
|
||||
def get_script():
|
||||
mode = request.args.get("mode", "full")
|
||||
script = build_full_script() if mode == "full" else build_default_script()
|
||||
level = request.args.get("level")
|
||||
if level is not None:
|
||||
try:
|
||||
script = build_script_for_level(int(level))
|
||||
except (ValueError, TypeError):
|
||||
script = build_default_script()
|
||||
elif mode == "full":
|
||||
script = build_full_script()
|
||||
else:
|
||||
script = build_default_script()
|
||||
return jsonify(script)
|
||||
|
||||
@app.route("/api/v1/session/upload", methods=["POST"])
|
||||
@@ -195,6 +205,70 @@ def register(app):
|
||||
return jsonify({"answer": answer})
|
||||
|
||||
|
||||
@app.route("/api/v1/elm/probe", methods=["POST"])
|
||||
def probe_elm():
|
||||
"""Пробинг ELM327: определение уровня устройства.
|
||||
|
||||
Принимает MAC и сырые ответы на команды пробинга от Android-клиента.
|
||||
Клиент посылает команды из списка, сервер классифицирует ответы.
|
||||
|
||||
Body: {
|
||||
"mac": "AA:BB:CC:...",
|
||||
"responses": [
|
||||
{"cmd": "ATE0", "raw": "OK"},
|
||||
{"cmd": "ATL0", "raw": "OK"},
|
||||
...
|
||||
]
|
||||
}
|
||||
|
||||
Returns: профиль устройства (level, supported, unsupported, ...)
|
||||
"""
|
||||
data = request.get_json(silent=True)
|
||||
if not data or "mac" not in data or "responses" not in data:
|
||||
return jsonify({"error": "missing 'mac' or 'responses'"}), 400
|
||||
|
||||
mac = data["mac"].strip()
|
||||
responses = data["responses"]
|
||||
|
||||
if not mac:
|
||||
return jsonify({"error": "empty mac"}), 400
|
||||
|
||||
# Классификация ответов через сервис
|
||||
from obd.classifier import determine_level
|
||||
|
||||
# Собираем ответы в словарь cmd→raw
|
||||
resp_map = {}
|
||||
for r in responses:
|
||||
cmd = (r.get("cmd") or "").strip().upper()
|
||||
raw = (r.get("raw") or "").strip()
|
||||
resp_map[cmd] = raw
|
||||
|
||||
result = determine_level(resp_map)
|
||||
result["mac"] = mac
|
||||
|
||||
_save_profile(mac, result)
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@app.route("/api/v1/elm/profile/<mac>", methods=["GET"])
|
||||
def get_elm_profile(mac: str):
|
||||
"""Возвращает сохранённый профиль устройства по MAC."""
|
||||
with Database() as db:
|
||||
p = db.get_device_profile(mac)
|
||||
if p is None:
|
||||
return jsonify({"error": "not found"}), 404
|
||||
return jsonify(p)
|
||||
|
||||
|
||||
def _save_profile(mac: str, profile: dict):
|
||||
"""Сохраняет профиль в БД (best-effort)."""
|
||||
try:
|
||||
with Database() as db:
|
||||
db.save_device_profile(mac, profile)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save device profile for {mac}: {e}")
|
||||
|
||||
|
||||
def _summary(p: dict) -> dict:
|
||||
return {
|
||||
"vin": p["vin"],
|
||||
|
||||
Reference in New Issue
Block a user