[2026-09-17] eagle: personal/projects/zont-config-compiler.md
This commit is contained in:
@@ -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` | 1245–1290 | ❌ **нет ветки `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 видит регресс там, где был
|
||||||
|
просто пропущенный шаг.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user