"""Эндпоинты DTC: расшифровка и сохранение кодов ошибок. POST /api/v1/dtc/decode — расшифровка кодов POST /api/v1/dtc/upload — приём скана ошибок """ import logging from pathlib import Path from flask import jsonify, request from api.db import Database logger = logging.getLogger("elmer.dtc") # Загрузка справочника DTC _DTC_CODES: dict[str, str] = {} _DTC_PATH = Path(__file__).parent.parent / "doc" / "dtc_codes.txt" def _load_dtc_dict() -> dict[str, str]: """Загрузить справочник DTC-кодов из doc/dtc_codes.txt.""" global _DTC_CODES if _DTC_CODES: return _DTC_CODES if _DTC_PATH.exists(): for line in _DTC_PATH.read_text(encoding="utf-8").splitlines(): line = line.strip() if "=" in line and not line.startswith("#"): code, desc = line.split("=", 1) _DTC_CODES[code.strip()] = desc.strip() return _DTC_CODES def register(app): """Регистрирует DTC-эндпоинты на Flask-приложении.""" @app.route("/api/v1/dtc/decode", methods=["POST"]) def dtc_decode(): """Расшифровка кодов DTC из справочника doc/dtc_codes.txt. Принимает: {"codes": ["P0301", "P0302"]} Возвращает: {"codes": [{"code": "P0301", "desc": "..."}]} Неизвестные коды возвращаются как есть — справочник неполный. """ data = request.get_json(silent=True) or {} codes = data.get("codes", []) if not codes: return jsonify({"codes": []}) dtc = _load_dtc_dict() decoded = [{"code": c.strip().upper(), "desc": dtc.get(c.strip().upper(), c)} for c in codes] return jsonify({"codes": decoded}) @app.route("/api/v1/dtc/upload", methods=["POST"]) def dtc_upload(): """Приём кодов ошибок (без полной диагностики, только Ошибки). Идемпотентность: если client шлёт request_id, сервер: 1. Проверяет кэш — если есть, возвращает его (LLM не вызывается) 2. Если нет — сохраняет, кэширует ответ Это предотвращает дубликаты при повторных отправках (например, 499). """ data = request.get_json(silent=True) if not data: return jsonify({"error": "empty"}), 400 codes = data.get("dtc_codes", []) client_info = data.get("client_info", {}) client_info["client_ip"] = request.remote_addr # Идемпотентность request_id = (data.get("request_id") or "").strip() if request_id: with Database() as db: cached = db.get_cached_response(request_id) if cached is not None: return jsonify(cached), 200 try: with Database() as db: db.save_dtc_scan(client_info, codes) except Exception as e: logger.error(f"DTC upload failed: {e}") dtc = _load_dtc_dict() decoded = [{"code": c, "desc": dtc.get(c.strip().upper(), c)} for c in codes] response = {"codes": decoded, "count": len(decoded)} # Кэш для идемпотентности if request_id: try: with Database() as db: db.save_session( client_info=client_info, responses=[{"decoded": f"DTC stored: {c}"} for c in codes], request_id=request_id, response_json=response, ) except Exception as e: logger.error(f"DTC idempotency cache failed: {e}") return jsonify(response)