diff --git a/API.md b/API.md index f5d614c..b02d8e5 100644 --- a/API.md +++ b/API.md @@ -1,72 +1,3 @@ - - -

Отличная задача! Я обновил вашу документацию, интегрировав в неё рабочие примеры кода на Python (основанные на вашем тестовом скрипте) и добавил реальные примеры JSON-ответов с детальной расшифровкой каждого поля. Это сделает API кристально понятным для фронтендеров и интеграторов.

Ниже представлена готовая обновленная версия документации.


📚 Документация API проекта: Excavators AI

Базовый URL: https://ex.neimarker.ru/api/

Формат дат: ISO 8601 (например, 2026-04-20T15:30:00Z)

Аутентификация (Для интеграций): Заголовок Authorization: Api-Key <токен>

Аутентификация (Web): Заголовок Authorization: Bearer <jwt_access_token>

📑 Оглавление

  1. Аутентификация и интеграция

  2. Экскаваторы (Базовый CRUD)

  3. Настройки графика и работы экскаватора

  4. Аналитика и Цифровой двойник

  5. Видеоархив, Карты и Очистка данных


<a name="auth"></a>

1. Аутентификация и интеграция

В системе предусмотрено два типа доступа: для живых пользователей (через веб-интерфейс с логином/паролем) и для сторонних систем (интеграция по статичному API-ключу).

⚠️ Важно: Для получения ключа интеграции (Api-Key) вашей организации необходимо обратиться в службу технической поддержки или к менеджеру.

💻 Базовая настройка клиента на Python

Python
import requests
-
-BASE_URL = "https://ex.neimarker.ru/api/"
-API_KEY = "ВАШ_УНИКАЛЬНЫЙ_ТОКЕН_ИНТЕГРАЦИИ" 
-
-headers = {
-    "Authorization": f"Api-Key {API_KEY}"
-}
-

<a name="devices_crud"></a>

2. Экскаваторы (Базовый CRUD)

Автоматически ограничивает выдачу только устройствами, принадлежащими организации, чей токен совершает запрос.

-Метод | Эндпоинт | Описание --- | -- | -- -GET | /devices/ | Список всех устройств организации. -POST | /devices/ | Создание устройства. Ожидает: name, bucket_volume. MAC и API-токен генерируются автоматически. -GET | /devices// | Данные одного устройства. -PATCH | /devices// | Изменение параметров (объем ковша, смены, часовой пояс timezone_offset и т.д.). - -

💻 Пример: Получение папок и детекций нейросети

Python
# Получаем часовые отрезки (папки)
-folders = requests.get(f"{BASE_URL}devices/43/folders/", headers=headers).json()
-folder_id = folders[0]['id']
-
-# Получаем Bounding Boxes для конкретного отрезка видео
-detections = requests.get(f"{BASE_URL}devices/43/folders/{folder_id}/detections/", headers=headers).json()
-

Пример ответа /folders/:

JSON
[
-  {
-    "id": 5495,
-    "process_after": "2026-04-23T15:02:00+03:00",
-    "received_at": "2026-04-23T15:18:28.541403+03:00",
-    "video_file": "/media/devices/43/2026/04/23/15/video_7d4b6624.mp4",
-    "file_count": 4350,
-    "original_width": 480,
-    "original_height": 640
-  }
-]
-

Интерпретация:

  • process_after: Временная метка начала данного часового отрезка.

  • video_file: Относительный путь до сшитого файла mp4.

  • file_count: Количество исходных кадров, вошедших в это видео (используется для расчета реального FPS).

Пример ответа /detections/:

JSON
{
-  "1": [
-    ["u00003", 0.3593, 0.2955, 0.5738, 0.5464, 0.97, null]
-  ],
-  "2": [
-    ["u00003", 0.3579, 0.2975, 0.5685, 0.5471, 0.97, null]
-  ]
-}
-

Интерпретация:

  • Ключи словаря ("1", "2") — это номера кадров (фреймов) в видеофайле.

  • Значения — массив детекций. Формат: [class_name, x_min, y_min, x_max, y_max, confidence, tracking_id].

  • Координаты x и y нормализованы от 0.0 до 1.0. Для отрисовки на канвасе их нужно умножить на ширину и высоту видеоплеера.

💻 Пример: Получение GPS-траектории и видео-кадра

Python
# Запрос GPS трека за период
-traj_params = {"start_date": start_date, "end_date": end_date}
-trajectory = requests.get(f"{BASE_URL}devices/43/trajectory/", headers=headers, params=traj_params).json()
-
-# Запрос точного видео-кадра на конкретное время
-frame_params = {"timestamp": "2026-04-23T12:00:00Z"}
-video_frame = requests.get(f"{BASE_URL}devices/43/video-frame/", headers=headers, params=frame_params).json()
-

Пример ответа /trajectory/:

JSON
[
-  { "ts": "2026-04-23T03:17:00.796000Z", "lat": 56.2350, "lon": 43.6361 },
-  { "ts": "2026-04-23T03:18:00.997000Z", "lat": 56.2350, "lon": 43.6361 }
-]
-

(Массив точек с координатами широты lat и долготы lon и временной меткой ts для рендеринга полилинии на картах Yandex/Google/Leaflet).

Пример ответа /video-frame/:

JSON
{
-  "video_url": "http://ex.neimarker.ru/media/devices/43/.../video_7d4b6624.mp4",
-  "seek_seconds": 182.875,
-  "folder_id": 5495,
-  "frame_index": 4389,
-  "debug_fps": 24.0
-}
-

Интерпретация:

Этот эндпоинт позволяет кликнуть на график аналитики или на карту, и мгновенно найти нужное место в видеоархиве.

  • video_url: Ссылка на нужный часовой отрезок видео.

  • seek_seconds: Точное время в секундах, на которое нужно перемотать HTML5 video плеер (video.currentTime = 182.875).

- -Отличная задача\! Я обновил вашу документацию, интегрировав в неё рабочие примеры кода на Python (основанные на вашем тестовом скрипте) и добавил реальные примеры JSON-ответов с детальной расшифровкой каждого поля. Это сделает API кристально понятным для фронтендеров и интеграторов. - -Ниже представлена готовая обновленная версия документации. - ------ - # 📚 Документация API проекта: Excavators AI > **Базовый URL:** `https://ex.neimarker.ru/api/` @@ -75,42 +6,48 @@ video_frame = requests.get(\ - -## 1\. Аутентификация и интеграция - + +## 1. Аутентификация и интеграция В системе предусмотрено два типа доступа: для живых пользователей (через веб-интерфейс с логином/паролем) и для сторонних систем (интеграция по статичному API-ключу). -> ⚠️ **Важно:** Для получения ключа интеграции (`Api-Key`) вашей организации необходимо обратиться в службу технической поддержки или к менеджеру. +### 🔑 Доступ для сторонних систем (Интеграция) +Для работы внешних скриптов, 1С, Telegram-ботов и других систем (M2M) капча и временные токены не используются. -### 💻 Базовая настройка клиента на Python +> ⚠️ **Важно:** Для получения ключа интеграции (`Api-Key`) вашей организации необходимо обратиться в службу технической поддержки или к менеджеру. +### 💻 Пример на Python: Интеграция по Api-Key ```python import requests BASE_URL = "https://ex.neimarker.ru/api/" +# Токен, выданный для вашей организации API_KEY = "ВАШ_УНИКАЛЬНЫЙ_ТОКЕН_ИНТЕГРАЦИИ" headers = { "Authorization": f"Api-Key {API_KEY}" } + +# Пример 1: Запрашиваем список всех доступных экскаваторов вашей организации +devices_res = requests.get(f"{BASE_URL}/devices/", headers=headers) +print("Доступная техника:", devices_res.json()) + +# Пример 2: Запрашиваем аналитику по одному экскаватору +analytics_res = requests.get( + f"{BASE_URL}/analytics/?device_id=1&start_date=2026-04-20T00:00:00Z&end_date=2026-04-20T23:59:59Z", + headers=headers +) +print("Аналитика:", analytics_res.json()) ``` - ------ - -\\ - -## 2\. Экскаваторы (Базовый CRUD) - +--- + +## 2. Экскаваторы (Базовый CRUD) Автоматически ограничивает выдачу только устройствами, принадлежащими организации, чей токен совершает запрос. | Метод | Эндпоинт | Описание | @@ -119,292 +56,73 @@ headers = { | `POST` | `/devices/` | Создание устройства. Ожидает: `name`, `bucket_volume`. MAC и API-токен генерируются автоматически. | | `GET` | `/devices//` | Данные одного устройства. | | `PATCH`| `/devices//` | Изменение параметров (объем ковша, смены, часовой пояс `timezone_offset` и т.д.). | +| `DELETE`| `/devices//`| Удаление. (Можно удалять только устройства с MAC "OFFLINE-..."). | -### 💻 Пример использования и ответ API +--- -```python -# Получение списка доступных экскаваторов -response = requests.get(f"{BASE_URL}devices/", headers=headers) -devices = response.json() -``` - -**Пример ответа (`/devices/` или `/devices//`):** - -```json -[ - { - "id": 43, - "name": "Excavator fd7f", - "mac_address": "24f40acefd7f", - "last_seen": "2026-04-23T15:18:35.744056+03:00", - "is_active": true, - "api_token": "f689c720828083b6...", - "bucket_volume": 2.5, - "shift_start": "00:00:00", - "shift_end": "23:59:00", - "work_days": 127, - "frame_width": 640, - "timezone_offset": 0, - "max_cycle_time_sec": 60, - "downtime_threshold_sec": 300, - "organization_name": "Дзержинск карьер", - "has_multiple_shifts": true - } -] -``` - -**Интерпретация полей:** - - * `last_seen`: Время последнего выхода устройства на связь (Heartbeat). - * `is_active`: Статус активности (поступает ли видео/телеметрия). - * `api_token`: Внутренний токен самого аппаратно-программного комплекса на экскаваторе. - * `bucket_volume`: Объем ковша в кубических метрах (используется для расчетов базовой аналитики). - * `has_multiple_shifts`: Флаг, указывающий, работает ли экскаватор в несколько смен (влияет на группировку аналитики). - * `downtime_threshold_sec`: Порог в секундах, после которого бездействие считается простоем (в данном случае 5 минут). - ------ - -\\ - -## 3\. Настройки графика и работы экскаватора - -Вложенные справочники для конкретного `device_id`. + +## 3. Настройки графика и работы экскаватора +Вложенные справочники для конкретного `device_id`. Поддерживают создание, чтение списка и удаление по `pk` (ID записи). | Метод | Эндпоинт | Описание | |---|---|---| -| `GET`, `POST` | `/devices//materials/` | Типы грунта (плотность, коэффициенты). | -| `GET`, `POST` | `/devices//shifts/` | Настройка смен (при `has_multiple_shifts=True`). | - -### 💻 Пример: Получение материалов и смен +| `GET`, `POST` | `/devices//materials/` | Типы грунта (плотность, коэффициенты) на заданный период. | +| `DELETE` | `/devices//materials//`| Удалить настройку материала. | +| `GET`, `POST` | `/devices//schedule-exceptions/` | Исключения графика: ТО, ремонты, выходные, спец-смены. | +| `DELETE` | `/devices//schedule-exceptions//` | Удалить исключение. | +| `GET`, `POST` | `/devices//breaks/` | Ежедневные перерывы (например, Обед 12:00 - 13:00). | +| `DELETE` | `/devices//breaks//` | Удалить перерыв. | +| `GET`, `POST` | `/devices//shifts/` | Настройка смен (при `has_multiple_shifts=True` у устройства). | +| `DELETE` | `/devices//shifts//` | Удалить смену. | +### 💻 Пример на Python: Настройка расписания ```python -device_id = 43 +device_id = 1 +headers = {"Authorization": f"Api-Key {API_KEY}"} -# Получаем настроенные материалы -materials = requests.get(f"{BASE_URL}devices/{device_id}/materials/", headers=headers).json() +# 1. Добавляем обед +requests.post(f"{BASE_URL}/devices/{device_id}/breaks/", headers=headers, json={ + "start_time": "12:00:00", + "end_time": "13:00:00", + "description": "Обеденный перерыв" +}) -# Получаем настроенные смены -shifts = requests.get(f"{BASE_URL}devices/{device_id}/shifts/", headers=headers).json() +# 2. Добавляем исключение (выходной / праздник) +requests.post(f"{BASE_URL}/devices/{device_id}/schedule-exceptions/", headers=headers, json={ + "date": "2026-05-01", + "end_date": "2026-05-01", + "is_working": False, + "is_maintenance": False, + "description": "День Труда" +}) ``` -**Пример ответа `/materials/`:** +--- -```json -[ - { - "id": 3, - "name": "Грунт", - "is_default": true, - "bulk_density": 1650.0, - "true_density": 2600.0, - "loosening_factor": 1.58 - } -] -``` - -**Интерпретация:** - - * `bulk_density`: Насыпная плотность (кг/м³). Используется нейросетью для перевода объема (м³) в массу (тонны). - * `loosening_factor`: Коэффициент разрыхления породы. - -**Пример ответа `/shifts/`:** - -```json -[ - { "id": 4, "name": "Дневная", "start_time": "08:00:00", "end_time": "20:00:00" }, - { "id": 5, "name": "Ночная", "start_time": "20:00:00", "end_time": "08:00:00" } -] -``` - ------ - -\\ - -## 4\. Аналитика и Цифровой двойник - -Ключевой эндпоинт для получения сводных данных о работе экскаватора за выбранный период. + +## 4. Аналитика и Цифровой двойник +Тяжелые вычислительные эндпоинты для получения отчетов и метрик. | Метод | Эндпоинт | Описание | |---|---|---| -| `GET` | `/analytics/` | Сводная аналитика. Требует параметры `?device_id=...&start_date=...&end_date=...`. | +| `GET` | `/analytics/` | Сводная аналитика. Требует параметры `?device_id=...&start_date=...&end_date=...`. Возвращает объемы, массу, таймлайн, циклы, телеметрию (JSON). | +| `POST` | `/analytics/recalculate/` | Полный пересчет аналитики (сборка циклов и простоев). Ожидает JSON: `device_id`, `start_date`. Запускается в фоне. | -### 💻 Пример: Запрос аналитики за сегодня +--- -```python -from datetime import datetime, time, timezone - -today = datetime.now(timezone.utc).date() -start_date = datetime.combine(today, time.min, tzinfo=timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') -end_date = datetime.combine(today, time.max, tzinfo=timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') - -params = { - "device_id": 43, - "start_date": start_date, - "end_date": end_date -} -analytics = requests.get(f"{BASE_URL}analytics/", headers=headers, params=params).json() -``` - -**Пример ответа `/analytics/`:** - -```json -{ - "total_mileage_km": 1.95, - "cycle_count_total": 675, - "cycle_count_vehicle": 266, - "cycle_count_ground": 409, - "estimated_volume_total": 1687.5, - "ai_volume_total": 1994.32, - "durations_by_class_sec": { - "full_bucket": 4019.7, - "unloading_vehicle": 2036.9, - "unloading_ground": 4897.2, - "empty_bucket": 9006.3, - "actual_downtime": 5233.1, - "no_data": 43205.6 - }, - "shift_stats": { - "Дневная": { - "cycles_total": 675, - "volume_total": 1687.5, - "actual_downtime": 5233.1 - }, - "Ночная": { - "cycles_total": 0, - "volume_total": 0.0 - } - }, - "averages": { - "vehicle": { - "empty_sec": 5.1, - "full_sec": 10.6, - "unload_sec": 6.5, - "total_sec": 22.1 - } - }, - "timeline": [ - { - "start": "2026-04-23T05:00:00.333000+00:00", - "end": "2026-04-23T05:00:03.978000+00:00", - "class_id": 1 - } - ] -} -``` - -**Интерпретация ключевых метрик:** - - * **Циклы и объемы:** Разделяются на `_vehicle` (погрузка в самосвал) и `_ground` (пересыпка на грунт). - * `estimated_volume_total`: Базовый объем (количество циклов × `bucket_volume` из настроек устройства). - * `ai_volume_total`: Умный расчет объема нейросетью (учитывает степень заполненности ковша в каждом цикле). - * `durations_by_class_sec`: Агрегированное время в секундах, проведенное машиной в разных состояниях (полный ковш, разгрузка, пустой ковш, фактический простой). - * `averages`: Среднее время (в секундах) затраченное на каждый этап рабочего цикла. - * `timeline`: Массив сырых событий для отрисовки диаграммы Ганта на фронтенде. `class_id` соответствует состояниям нейросети. - ------ - -\\ - -## 5\. Видеоархив, Карты и Очистка данных - -Работа с сырыми видеофайлами, детекциями нейросети (Bounding Boxes) и маршрутом (GPS). + +## 5. Видеоархив, Карты и Очистка данных +Все, что связано с видео, треками и управлением сырыми данными устройства. | Метод | Эндпоинт | Описание | |---|---|---| -| `GET` | `/devices//folders/` | Список папок архива (часовых отрезков видео). | -| `GET` | `/devices//folders//detections/` | Координаты рамок (Bounding Boxes) для плеера. | -| `GET` | `/devices//trajectory/`| Трек маршрута (GPS). | -| `GET` | `/devices//video-frame/`| Синхронизация времени с точным кадром видео. | +| `GET` | `/devices//available-dates/` | Массив дат (YYYY-MM-DD), за которые есть загруженные данные. | +| `GET` | `/devices//folders/` | Список папок архива (кусков видео по 1 часу). | +| `GET` | `/devices//folders//detections/` | Координаты рамок (Bounding Boxes) для рисования поверх видеоплеера. | +| `GET` | `/devices//video-frame/?timestamp=...` | Поиск видеофайла и секунды для перемотки по точному времени. | +| `GET` | `/devices//trajectory/?start_date=...&end_date=...`| Координаты GPS для отрисовки линии маршрута на карте. | +| `POST`| `/devices//upload-archive/` | Ручная загрузка оффлайн `.zip` архива. Возвращает Streaming HTTP с логами распаковки. | +| `GET` | `/devices//alerts/` | Список последних экстренных событий (Алертов) от устройства. | +| `POST`| `/devices//clear-data/` | **Опасная операция.** Очистка данных устройства. JSON: `{"mode": "all"}` или `{"mode": "date", "date": "..."}`. | -### 💻 Пример: Получение папок и детекций нейросети - -```python -# Получаем часовые отрезки (папки) -folders = requests.get(f"{BASE_URL}devices/43/folders/", headers=headers).json() -folder_id = folders[0]['id'] - -# Получаем Bounding Boxes для конкретного отрезка видео -detections = requests.get(f"{BASE_URL}devices/43/folders/{folder_id}/detections/", headers=headers).json() -``` - -**Пример ответа `/folders/`:** - -```json -[ - { - "id": 5495, - "process_after": "2026-04-23T15:02:00+03:00", - "received_at": "2026-04-23T15:18:28.541403+03:00", - "video_file": "/media/devices/43/2026/04/23/15/video_7d4b6624.mp4", - "file_count": 4350, - "original_width": 480, - "original_height": 640 - } -] -``` - -**Интерпретация:** - - * `process_after`: Временная метка начала данного часового отрезка. - * `video_file`: Относительный путь до сшитого файла `mp4`. - * `file_count`: Количество исходных кадров, вошедших в это видео (используется для расчета реального FPS). - -**Пример ответа `/detections/`:** - -```json -{ - "1": [ - ["u00003", 0.3593, 0.2955, 0.5738, 0.5464, 0.97, null] - ], - "2": [ - ["u00003", 0.3579, 0.2975, 0.5685, 0.5471, 0.97, null] - ] -} -``` - -**Интерпретация:** - - * Ключи словаря (`"1"`, `"2"`) — это номера кадров (фреймов) в видеофайле. - * Значения — массив детекций. Формат: `[class_name, x_min, y_min, x_max, y_max, confidence, tracking_id]`. - * Координаты `x` и `y` нормализованы от `0.0` до `1.0`. Для отрисовки на канвасе их нужно умножить на ширину и высоту видеоплеера. - -### 💻 Пример: Получение GPS-траектории и видео-кадра - -```python -# Запрос GPS трека за период -traj_params = {"start_date": start_date, "end_date": end_date} -trajectory = requests.get(f"{BASE_URL}devices/43/trajectory/", headers=headers, params=traj_params).json() - -# Запрос точного видео-кадра на конкретное время -frame_params = {"timestamp": "2026-04-23T12:00:00Z"} -video_frame = requests.get(f"{BASE_URL}devices/43/video-frame/", headers=headers, params=frame_params).json() -``` - -**Пример ответа `/trajectory/`:** - -```json -[ - { "ts": "2026-04-23T03:17:00.796000Z", "lat": 56.2350, "lon": 43.6361 }, - { "ts": "2026-04-23T03:18:00.997000Z", "lat": 56.2350, "lon": 43.6361 } -] -``` - -*(Массив точек с координатами широты `lat` и долготы `lon` и временной меткой `ts` для рендеринга полилинии на картах Yandex/Google/Leaflet).* - -**Пример ответа `/video-frame/`:** - -```json -{ - "video_url": "http://ex.neimarker.ru/media/devices/43/.../video_7d4b6624.mp4", - "seek_seconds": 182.875, - "folder_id": 5495, - "frame_index": 4389, - "debug_fps": 24.0 -} -``` - -**Интерпретация:** -Этот эндпоинт позволяет кликнуть на график аналитики или на карту, и мгновенно найти нужное место в видеоархиве. - - * `video_url`: Ссылка на нужный часовой отрезок видео. - * `seek_seconds`: Точное время в секундах, на которое нужно перемотать HTML5 `video` плеер (`video.currentTime = 182.875`). \ No newline at end of file +--- \ No newline at end of file