126 lines
4.8 KiB
Python
126 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Elmer — главный вход.
|
|
|
|
Порядок работы:
|
|
1. Подключается к ELM327 по Bluetooth
|
|
2. Читает VIN
|
|
3. Читает коды ошибок (stored + pending)
|
|
4. Читает базовые параметры ЭБУ
|
|
5. Отправляет в DeepSeek
|
|
6. Печатает диагноз
|
|
7. Сохраняет всё в SQLite
|
|
|
|
Использование:
|
|
DEEPSEEK_API_KEY=sk-... python run.py # полный цикл
|
|
python run.py --no-llm # только чтение данных (без LLM)
|
|
python run.py --no-llm --port /dev/rfcomm0 # свой порт
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from elmer.config import load
|
|
from elmer.db import Database
|
|
from elmer.diagnose import Diagnoser
|
|
from elmer.elm import ELM327
|
|
from elmer.prompts import SYSTEM_PROMPT, build_user_prompt
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Elmer — диагностика авто через ELM327 + LLM")
|
|
parser.add_argument("--no-llm", action="store_true", help="Только чтение данных, без LLM")
|
|
parser.add_argument("--port", help="Порт ELM327 (по умолчанию из config.yaml)")
|
|
args = parser.parse_args()
|
|
|
|
config = load()
|
|
|
|
# ── 1. ELM327 ────────────────────────────────────
|
|
print("🔌 Подключение к ELM327...")
|
|
elm_cfg = config["elm327"]
|
|
port = args.port or elm_cfg["port"]
|
|
|
|
try:
|
|
elm = ELM327(port=port, baudrate=elm_cfg["baudrate"])
|
|
except Exception as e:
|
|
print(f"❌ Не удалось открыть порт {port}: {e}")
|
|
print(" Проверь Bluetooth-сопряжение:")
|
|
print(" bash scripts/setup-bt.sh")
|
|
sys.exit(1)
|
|
|
|
if not elm.init():
|
|
print("❌ ELM327 не ответил на ATZ. Проверь соединение.")
|
|
elm.close()
|
|
sys.exit(1)
|
|
print("✅ ELM327 готов")
|
|
|
|
# ── 2. VIN ───────────────────────────────────────
|
|
print("🚗 Чтение VIN...")
|
|
vin = elm.read_vin()
|
|
if not vin:
|
|
print("❌ Не удалось прочитать VIN. Машина поддерживает режим 09?")
|
|
elm.close()
|
|
sys.exit(1)
|
|
print(f"✅ VIN: {vin}")
|
|
|
|
# ── 3. Коды ошибок ──────────────────────────────
|
|
print("🔍 Чтение кодов ошибок...")
|
|
dtc_stored = elm.read_dtc_codes("03")
|
|
dtc_pending = elm.read_dtc_codes("07")
|
|
dtc_codes = dtc_stored + dtc_pending
|
|
if dtc_codes:
|
|
for dtc in dtc_codes:
|
|
print(f" ⚠️ {dtc['code']} [{dtc['status']}]")
|
|
else:
|
|
print(" ✅ Ошибок нет")
|
|
|
|
# ── 4. Параметры ЭБУ ────────────────────────────
|
|
print("📊 Чтение параметров...")
|
|
parameters = elm.read_all_pids(config.get("pids"))
|
|
for p in parameters:
|
|
print(f" {p['name']}: {p['value']} {p['unit']}")
|
|
|
|
elm.close()
|
|
|
|
# ── 5. Сохранение в SQLite (всегда) ──────────────
|
|
db = Database()
|
|
car_id = db.get_or_create_car(vin)
|
|
token_id = db.create_token(car_id)
|
|
for dtc in dtc_codes:
|
|
db.add_dtc(token_id, dtc["code"], dtc.get("description", ""), dtc["status"])
|
|
for p in parameters:
|
|
db.add_parameter(token_id, p["pid_code"], p["name"], p["value"], p["unit"])
|
|
print(f"\n💾 Сохранено в elmer.db (token_id={token_id})")
|
|
|
|
# ── 6. DeepSeek (опционально) ────────────────────
|
|
if args.no_llm:
|
|
print("\n⏭️ Пропуск LLM (--no-llm). Данные сохранены в elmer.db")
|
|
return
|
|
|
|
api_key = config["deepseek"]["api_key"]
|
|
if not api_key:
|
|
print("\n❌ DEEPSEEK_API_KEY не задан.")
|
|
print(" Установи: export DEEPSEEK_API_KEY=sk-...")
|
|
print(" Или запусти без LLM: python run.py --no-llm")
|
|
return
|
|
|
|
print("\n🧠 Отправка в DeepSeek...")
|
|
diagnoser = Diagnoser(
|
|
api_key=api_key,
|
|
model=config["deepseek"].get("model", "deepseek-chat"),
|
|
)
|
|
|
|
user_prompt = build_user_prompt(vin, dtc_codes, parameters)
|
|
answer = diagnoser.diagnose(SYSTEM_PROMPT, user_prompt)
|
|
|
|
print("\n" + "=" * 60)
|
|
print(answer)
|
|
print("=" * 60)
|
|
|
|
db.add_llm_message(token_id, "user", user_prompt)
|
|
db.add_llm_message(token_id, "assistant", answer)
|
|
print(f"\n💾 LLM-ответ сохранён (token_id={token_id})")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|