Перейти к содержанию

API — Дозаторы

Управление и мониторинг трёх каналов дозирования. Логика алгоритма — ../dosing-logic.md.

Все эндпоинты используют ch ∈ {0, 1, 2} для адресации канала.

Источник: src/API/Dosator/.


GET /api/dosator/status

Текущее состояние всех трёх каналов одним запросом. Опрашивается веб-интерфейсом каждые 2 секунды.

Роль: USER+ (любая авторизация).

Запрос

curl -u duty:duty123 http://awdc.local/api/dosator/status

Ответ — 200 OK

{
  "channels": [
    {
      "ch": 0,
      "proportion": 22,
      "window_ms": 2000,
      "mode": 0,
      "run": true,
      "lev": true,
      "out": true,
      "force": false,
      "runtime_s": 38421,
      "pump_s": 14203
    },
    {
      "ch": 1,
      "proportion": 50,
      "window_ms": 2000,
      "run": false,
      "lev": false,
      "out": false,
      "force": false,
      "runtime_s": 12080,
      "pump_s": 6040
    },
    {
      "ch": 2,
      "proportion": 0,
      "window_ms": 2000,
      "run": false,
      "lev": true,
      "out": false,
      "force": false,
      "runtime_s": 0,
      "pump_s": 0
    }
  ]
}
Поле Тип Описание
ch int Индекс канала (0…2)
proportion int Текущая пропорция ШИМ (0…99)
window_ms int Длина окна цикла, мс (100…60000)
mode int Режим дозирования: 0 пропорциональный, 1 импульсный
run bool Внешний сигнал «идёт мойка» (digitalRead(pinRun) == HIGH)
lev bool Датчик уровня концентрата (с антидребезгом 3 тика)
out bool Канал в состоянии DOSING (активно крутит ШИМ; не мгновенное состояние пина)
force bool Принудит. режим (forceActive)
runtime_s int Lifetime RUN-наработка из EEPROM + текущая сессия, секунд
pump_s int Lifetime время насоса из EEPROM + текущая сессия, секунд

POST /api/dosator/config

Изменить пропорцию, окно цикла и/или режим канала. Записывается в NVS немедленно.

Роль: DUTY+.

Запрос

curl -u duty:duty123 -X POST \
     -H "Content-Type: application/json" \
     -d '{"ch":0,"proportion":25}' \
     http://awdc.local/api/dosator/config

Тело

{
  "ch": 0,
  "proportion": 25,    // опционально, 0..99
  "window_ms": 2000,   // опционально, 100..60000
  "mode": 0            // опционально, 0 пропорциональный / 1 импульсный
}

Поля тела:

  • ch (int, обязательно) — индекс канала 0…2.
  • proportion (int) — 0…99 и < window_ms / 20.
  • window_ms (int) — 100…60000.
  • mode (int) — 0 пропорциональный, 1 импульсный.
  • enabled (bool) — канал подключён; выключенному не вешается RUN-ISR.

Минимум одно из proportion / window_ms / mode / enabled обязательно — иначе 400.

Выключенный канал (enabled:false): задача простаивает, RUN-прерывание снимается (висящий вход неподключённого дозатора не наводит ложных срабатываний), выход держится LOW. В Web UI карточка канала скрыта, HMI пропускает его при переключении кнопками.

Ответ — 200 OK

{
  "success": true,
  "ch": 0,
  "proportion": 25,
  "window_ms": 2000,
  "mode": 0
}

Ошибки

Код Тело
400 {"success":false,"message":"Invalid JSON"}
400 {"success":false,"message":"ch must be 0..2"}
400 {"success":false,"message":"proportion must be 0..99"}
400 {"success":false,"message":"proportion must be < window_ms/20"}
400 {"success":false,"message":"window_ms must be 100..60000"}
400 {"success":false,"message":"No valid fields"}
401 {"error":"Unauthorized"}
403 {"error":"Forbidden"}
413 {"success":false,"message":"Body too large"}

Эффекты

  • Запись в NVS namespace dosator: ключи prop_<ch>, window_ms_<ch>.
  • Событие DOSING_EVT_PROPORTION_CHANGE → HMI обновляет дисплей.
  • Если канал в данный момент DOSING — новая пропорция применяется со следующего тика (20 мс).

POST /api/dosator/dispense

Принудительный старт канала. Устанавливает forceActive = true — дозатор начинает ШИМ-цикл независимо от внешнего RUN-сигнала.

Роль: DUTY+.

Запрос

curl -u duty:duty123 -X POST \
     -H "Content-Type: application/json" \
     -d '{"ch":0}' \
     http://awdc.local/api/dosator/dispense

Тело

{"ch": 0}

Ответ — 200 OK

{"success": true}

Ошибки

Код Тело
400 {"success":false,"message":"Invalid JSON"}
400 {"success":false,"message":"ch must be 0..2"}
401 {"error":"Unauthorized"}
403 {"error":"Forbidden"}
413 {"success":false,"message":"Body too large"}

Замечания

  • Если proportion == 0 — канал останется IDLE, ничего не польётся.
  • Если lev == false (нет концентрата) — пин DOZ остаётся LOW, но канал в DOSING. Это нормально: warmup пропускается, Bresenham обнуляется через eff=0.
  • Чтобы понять, реально ли крутится цикл, смотри out в /api/dosator/status.

POST /api/dosator/stop

Принудительная остановка канала. Сбрасывает forceActive = false.

Роль: DUTY+.

Запрос

curl -u duty:duty123 -X POST \
     -H "Content-Type: application/json" \
     -d '{"ch":0}' \
     http://awdc.local/api/dosator/stop

Тело

{"ch": 0}

Ответ — 200 OK

{"success": true}

Ошибки

Те же коды, что у /dispense.

Замечания

  • Если канал был в DOSING из-за внешнего RUN (а не force) — /stop не остановит его. Force-флаг и RUN-сигнал независимы.
  • Чтобы заглушить «сейчас и навсегда» — proportion=0 через /api/dosator/config.

GET /api/statistics

Счётчики наработки по каналам — абсолютные (lifetime) и относительные (с последнего сброса на ТО). Источник — EEPROM (схема v3, две зоны), с аккумуляцией текущей сессии в RAM.

Роль: USER+.

Запрос

curl -u duty:duty123 http://awdc.local/api/statistics

Ответ — 200 OK

{
  "channels": [
    {"ch": 0, "enabled": true, "runtime_abs_s": 38421, "runtime_rel_s": 5021,
     "pump_abs_s": 14203, "pump_rel_s": 1820, "reset_ts": 1779000000, "starts": 12},
    {"ch": 1, "enabled": true, "runtime_abs_s": 12080, "runtime_rel_s": 12080,
     "pump_abs_s": 6040, "pump_rel_s": 6040, "reset_ts": 0, "starts": 4},
    {"ch": 2, "enabled": false, "runtime_abs_s": 0, "runtime_rel_s": 0,
     "pump_abs_s": 0, "pump_rel_s": 0, "reset_ts": 0, "starts": 0}
  ]
}
  • ch (int) — индекс канала (0…2).
  • enabled (bool) — канал подключён/активен.
  • runtime_abs_s (int) — абсолютная RUN-наработка, секунд (lifetime + текущая сессия).
  • runtime_rel_s (int) — RUN-наработка с последнего сброса на ТО, секунд.
  • pump_abs_s (int) — абсолютное время насоса (outN=HIGH), секунд.
  • pump_rel_s (int) — время насоса с последнего сброса на ТО, секунд.
  • reset_ts (int) — local epoch последнего сброса на ТО; 0 — сброса не было.
  • starts (int) — запусков DOSING за текущий uptime (RAM, обнуляется reboot).

Относительные счётчики = абсолютные − baseline (снимок на момент ТО).


POST /api/statistics/reset

Сброс на ТО одного канала: относительные счётчики (runtime_rel_s, pump_rel_s) обнуляются, абсолютные — сохраняются. В EEPROM пишется новый baseline (Zone B) с датой сброса.

Роль: ADMIN.

Запрос

curl -u admin:admin123 -X POST \
     -H 'Content-Type: application/json' -d '{"ch": 0}' \
     http://awdc.local/api/statistics/reset
  • ch (int) — индекс канала (0…2), обязателен.

Ответ — 200 OK

{"success": true, "ch": 0, "reset_ts": 1779046800}

Замечания

  • Операция неразрушающая для абсолютной наработки — обнуляется только относительный счётчик (через запись baseline в Zone B).
  • reset_ts — local epoch (UTC + tz_offset); 0 если время не синхронизировано по NTP.
  • Полное обнуление абсолютной наработки возможно только форматированием EEPROM — POST /api/diag/eeprom/format (защищён паролём администратора).