[2026-09-17] eagle: personal/projects/zont-config-compiler.md

This commit is contained in:
Alexey Martemyanov
2026-09-17 23:41:33 +06:00
parent b5d86445d7
commit 4736965eff
+154 -3
View File
@@ -3,7 +3,7 @@ title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml
namespace: personal namespace: personal
type: how-to type: how-to
created: '2026-09-17' created: '2026-09-17'
updated: '2026-09-17k43' updated: '2026-09-18'
tags: tags:
- personal - personal
- zont - zont
@@ -58,6 +58,15 @@ aliases:
- ZONT дубли id scenario_step - ZONT дубли id scenario_step
- ZONT curl config.txt - ZONT curl config.txt
- ZONT стянуть конфиг - ZONT стянуть конфиг
- ZONT авто-id
- ZONT автогенерация id
- ZONT next_id max+1
- ZONT objcmd
- ZONT objcmd source
- ZONT objcmd args
- ZONT три точки входа энкодера
- ZONT круг 44
- ZONT рабочее дерево 2026-09-18
related: related:
- '[[family/tech/zont-api]]' - '[[family/tech/zont-api]]'
- '[[family/how-to/home-automation]]' - '[[family/how-to/home-automation]]'
@@ -3075,9 +3084,12 @@ Alex прочитал `git show --stat ba6ef44` (2 файла, 10 строк) и
| 4 | ✅ **ЗАКРЫТО**`args: [0, 1]` у `#Z10069` была выдумкой; поле 5 записи 59 производное, энкодер ставит сам (§21.5, `4b99d8e`) | — | | 4 | ✅ **ЗАКРЫТО**`args: [0, 1]` у `#Z10069` была выдумкой; поле 5 записи 59 производное, энкодер ставит сам (§21.5, `4b99d8e`) | — |
| 5 | Поле 4 = `2` у `expr` (`10077``10085`) — то же производное поле, что поле 5 (§21.5). Имени не дано, энкодер восстанавливает по признаку «второй операнд — литерал» | форма не согласована | | 5 | Поле 4 = `2` у `expr` (`10077``10085`) — то же производное поле, что поле 5 (§21.5). Имени не дано, энкодер восстанавливает по признаку «второй операнд — литерал» | форма не согласована |
| 6 | Push в Gitea — **15** коммитов не запушены | по команде Alex | | 6 | Push в Gitea — **15** коммитов не запушены | по команде Alex |
| 7 | **`objcmd`: `args``source`** — форма согласована, в код НЕ внесена (§26) | делать (план готов) |
| 8 | **Авто-id** — согласовано, план готов (§25), не реализовано | делать (нужен ответ про верхнюю границу `20556`) |
> ✅ Единственный открытый пункт — 5 (поле 4 у `expr`), и он **не блокирует** круг: 9/9 зелёный, > ⚠️ Пункты **7** и **8** добавлены 2026-09-18 — оба согласованы, но не реализованы.
> все открытые ранее вопросы закрыты. Всё в этом доке проверено на 9 конфигах. > ✅ Единственный «нерешённый по форме» пункт — 5 (поле 4 у `expr`), и он **не блокирует** круг:
> 9/9 зелёный. Всё в этом доке проверено на 9 конфигах.
--- ---
@@ -3448,7 +3460,146 @@ echo "exit=$?"; ls -la zont_config/config_X.yml
| `#Z10447=*` | маркер пустого объекта, встречается в конфиге | | `#Z10447=*` | маркер пустого объекта, встречается в конфиге |
| Маска дней `109` | `0b1101101` = пн, ср, чт, сб, вс (§10.1 круг 16) | | Маска дней `109` | `0b1101101` = пн, ср, чт, сб, вс (§10.1 круг 16) |
---
## 24. 🔄 Рабочее дерево на начало сессии 2026-09-18 (НЕ закоммичено)
Состояние **отличается** от §21.2 (который описывает конец круга 43). Проверено `git log`/`git status`
в начале следующей сессии:
| | |
|---|---|
| **HEAD** | **`5fdedc9`** — «type 5 в шаге: action вместо хвоста params, отдельная секция сборки» |
| **`origin/main`** | **`4b99d8e`** → **`5fdedc9` НЕ запушен** |
| **Модифицированы** | `config-to-yml.py`, `yml-to-config.py`, `zont_config/config_local_2026-09-17_23-21-20.yml` |
| **Untracked** | `audit2.py`, `audit_8456.py`, `check_ops.py`, `chk_extra.py`, `dump_new_types.py`, `fit_temp.py`, `fit_temp2.py`, `probe_sched.py`, `probe_types.py`, `read_scenarios.py`, `trace_scenarios.py`, `verify_answers.py`, `why_raw.py`, `zont_api_docs/`, `zont_local_ui_recon/` |
| **Артефакт** | `config_local_2026-09-17_23-21-20.yml` — 90 918 байт, mtime `Sep 17 23:30:52` (на 55 байт меньше, чем в §21.2: **90 973 → 90 918**, расхождение не объяснено) |
| **В `zont_config/`** | два `.yml`: `..._22-26-26.yml` (90 480) и `..._23-21-20.yml` (90 918) |
⚠️ **Правило:** §21.2 описывает **коммиты**, §24 — **рабочее дерево**. На старте сессии — всегда
`git status`, не полагаться на «всё закоммичено».
🔴 **ПИТФОЛЛ 91 — код не является источником истины о своём поведении.** На вопрос «а разве в коде
уже нет авто-расчёта id?» я собирался ответить по памяти («нет, и это подтверждено grep'ом»).
Фактическая проверка: `grep -nE "auto|_next|next_id|max\(|alloc|assign" yml-to-config.py`**пусто**,
авто-id действительно нет. Но **находка рядом оказалась важнее**: `objcmd` собирается в **двух**
точках (`emit_step` ~990 и `emit_action` ~770), и покрыта была только одна. Гипотезу про код
проверять `grep` по **всем** точкам входа, а не по одной.
---
## 25. 🎯 План: авто-id для всех сущностей (СОГЛАСОВАНО, не реализовано)
**Запрос Alex:** «я предполагал что id генерятся автоматом для всех сущностей если не прописаны явно».
### 25.1. Состояние кода (проверено, не по памяти)
| Факт | Деталь |
|---|---|
| Аллокатора **нет** | `grep -nE "auto\|_next\|next_id\|max(\|alloc\|assign"` в `yml-to-config.py`**0 строк** |
| Есть только **читатель** id | `_collect_yaml_ids` (строка 68) собирает уже прописанные id в `_known_ids`, чтобы отличать литерал от ссылки. Если id нет — в множество не попадёт, дальше `obj['id']` падает |
| **17 сырых обращений** `obj['id']` | строки `105, 108, 149, 154, 161, 189, 198, 203, 210, 222, 225, 1213, 1219, 1230, 1235, 1242, 1268` — жёсткие, не `.get()` |
| Единственный «дефолт» | `config_id = config.get('id', 0)` (строка 99) — это **`0`**, не сгенерированный id. `#Z0=36,...` прибор не примет |
### 25.2. 🔴 Правило нумерации прибора — ВЫВЕДЕНО ИЗ ДАННЫХ
```
next_id = max(все существующие id) + 1 ← НЕ «первая свободная дыра»
```
**Доказательство:** Alex добавил 75 объектов через UI. Прибор выдал им id **`10371..10447` подряд,
без дыр**, продолжая от предыдущего максимума `10370`. Разрывы `10441→10443` и `10445→10447` — это
`10442`/`10446`, которые он **удалил** (остались в конфиге как `end`-терминаторы).
| Проверка | Результат |
|---|---|
| max id в конфиге | **`20555`** |
| id `20551`/`20554`/`20555` | **железо** (сопроцессоры, радиомодуль) — выдано прибором, не человеком |
| «пользовательские» id | диапазон `4098..12109`, плотно, с дырами |
| Дыр всего | **19 484** — прибор их **не использует** |
### 25.3. План
| # | Шаг | Оценка |
|---|---|---|
| 1 | Разведка: как `emit_step`/`emit_action`/`emit_condition` + `scenario_orphans` разрешают ссылки | 5 мин |
| 2 | Аллокатор: `_ids = _known_ids {id из всех секций}`; `_next = max(_ids)+1`; `alloc()` с инкрементом. **Фаза 1** — пройти секции и проставить id всем записям без `id` (записать **в саму запись**, чтобы ссылки нашли) | 20 мин |
| 3 | Ссылки на объекты без id — **вариант (в): разрешить только листья** (объект ни на что не ссылается). Покрывает ~90% (датчик, действие, регистр), не ломает модель ссылок | 30 мин, риск |
| 4 | Заменить 17 точек `obj['id']` → id из аллокатора. Простые: `relays`, `heating_circuits`, `sensors`, `analog_outputs`, `marker_entries`, `modbus_devices`. Сложные — сценарии (id нужен **до** эмиссии шагов) | 40 мин |
| 5 | Круг 9/9 — регресс обязателен (старые конфиги все с id, поведение не должно измениться) | 5 мин |
| 6 | Тест автогенерации: из артефакта удалить `id` у 3 объектов, собрать, проверить назначение и сходимость | 10 мин |
**Итого ~2 часа**, из них час — шаг 3.
**Риски:** (а) ссылки в новые объекты — реальная проблема, требует решения шага 3; (б) порядок
эмиссии: `S`-блоки первыми, `Z` — после, id нужен раньше строки; (в) **верхняя граница**
`max+1 = 20556` перескочит железо, вопрос к Alex открыт.
---
## 26. 🔴 `objcmd`: `args` — СТАРОЕ поле декодера, энкодер его молча проносит (2026-09-18)
**Жалоба Alex:** «я все еще вижу `args: - 8472`» — при том, что форма уже согласована как вариант A
(источник отдельным ключом, не `args`).
### 26.1. Что в артефакте и что в сырье
| id | Сырая строка | YAML сейчас |
|---|---|---|
| `10371` | `#Z10371=59,'objcmd 8700 "1 %0"',14.5,0,1` | `target: 8700`, `args: [14.5]` |
| `10375` | `#Z10375=59,'objcmd 9102 "6,%0";#a',10374,0,0` | `target: 9102`, `args: [10374]` |
| `10398` | `#Z10398=59,'objcmd 8382 "1 %0"',8472,0,0` | `target: 8382`, `args: [8472]` |
`args` — это **поле 3 записи**, прочитанное как «список аргументов». У `10371` — литерал `14.5`,
у `10398`**ссылка на объект** (`#Z8472=59,'set var1',0,0,0` существует). Одно имя `args` покрывает
и литерал, и ссылку → читатель не может отличить.
### 26.2. Корень: зеркало бага из §6 доки `roundtrip-key-verification`
**`objcmd` собирается в ТРЁХ точках, покрыта одна:**
| Точка | Строка `yml-to-config.py` | Статус |
|---|---|---|
| `emit_step` | ~990 | ✅ работает: `fields = [59, cmd, *args] + f5` |
| `emit_action` | ~770 | ❌ пропускает разобранную форму (ветка `'pickle' in node` ловит всё сырое) |
| Цикл добивки по `scenario_scripts` | 12451290 | ❌ **нет ветки `objcmd`**, `raw` не выставляется — работает **по стечению** (`_emitted_ids` уже содержит `10398`) |
**Почему круг зелёный:** `args` создаёт **декодер** (`config-to-yml.py:1011`,
`node['args'] = [_oc_arg]`), энкодер его читает через `step.get('args') or [0]` и байты доносит
верно. Round-trip доказывает «вывод парсера == вход», про энкодер не говорит ничего (см. док
`roundtrip-key-verification`).
### 26.3. Целевая форма (вариант A, согласована)
```yaml
- id: 10398
objcmd: set_sensor # имя действия (маска 1 %0)
cmd: objcmd 8382 "1 %0" # текст команды — читаемость + обратная сборка
target: 8382 # кому адресована команда
source: 8472 # ← ОТКУДА берётся значение (поле 3 записи)
```
У `10371` источник — **литерал**: `source: 14.5`. Одно поле, два типа значения; читатель видит
разницу сразу: `8472` есть в конфиге как объект, `14.5` — нет.
### 26.4. Шаги правки (~40 мин, риск низкий)
1. Декодер `config-to-yml.py:1011``node['source'] = _oc_arg` вместо `node['args'] = [_oc_arg]`. ~1 строка.
2. Энкодер `yml-to-config.py:1004` — читать `source`, не `args`; поле 5 по-прежнему производное
(`source ∈ _known_ids``0`, иначе `1`). ~3 строки.
3. Энкодер `emit_action:770` — добавить ветку `objcmd` (та же логика, что в `emit_step`).
4. Цикл добивки 1245–1290 — добавить ветку `objcmd`, чтобы объект не проходил по стечению.
5. Круг 9/9 → перегенерировать артефакт **в целевой файл** (§22.5, питфолл 87).
**Проверка — подмена, не read-back:** поменять `source: 8472``source: 8669` в **копии**, собрать,
убедиться, что изменилась **ровно одна строка** и в ней `8669`.
### 26.5. 🔴 ПИТФОЛЛ 92 — форма согласована ≠ в код внесена
Alex третий раз видел `args` у `objcmd` при том, что форма обсуждалась и была принята («пойдет»).
**Согласованную форму фиксировать в доке СРАЗУ при согласии, а правку кода делать в тот же заход.**
Иначе следующая сессия стартует с вопроса «почему всё ещё так», и Alex видит регресс там, где был
просто пропущенный шаг.
--- ---