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

This commit is contained in:
Alexey Martemyanov
2026-09-17 23:21:13 +06:00
parent bfee8e98ef
commit d5f6e1476a
+42 -3
View File
@@ -3,7 +3,7 @@ title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml
namespace: personal
type: how-to
created: '2026-09-17'
updated: '2026-09-17k40'
updated: '2026-09-17k42'
tags:
- personal
- zont
@@ -41,6 +41,12 @@ aliases:
- ZONT args выдуманный ключ
- ZONT круг 39
- ZONT круг 40
- ZONT круг 41
- ZONT круг 42
- ZONT шаг 5 хвост полей
- ZONT params выдуманный ключ
- ZONT value поле 10 действия
- ZONT запись типа 5
related:
- '[[family/tech/zont-api]]'
- '[[family/how-to/home-automation]]'
@@ -171,6 +177,29 @@ python3 test_roundtrip.py zont_config/config_X.txt
# 6. Залить /tmp/zont_new.txt в контроллер (см. §9)
```
### 🔴 Круг по ВСЕМ конфигам — без аргумента гоняется только свежий
`test_roundtrip.py` без аргумента берёт **только самый свежий** `.txt` из `zont_config/`.
Зелёный результат «по умолчанию» ≠ зелёный по всем снапшотам. Полный прогон:
```bash
cd /Users/admin/Automation/HA-ZONT-Modbus
pass=0; fail=0
for f in zont_config/config_local_2026-09-17_*.txt; do
out=$(python3 test_roundtrip.py "$f" 2>&1)
if echo "$out" | grep -q "ЧИСТЫЙ"; then
pass=$((pass+1)); echo "OK $(basename $f)"
else
fail=$((fail+1)); echo "FAIL $(basename $f)"
echo "$out" | grep -E "❌|расхожд|потеря|лишн" | head -5
fi
done
echo "=== pass=$pass fail=$fail"
```
Правило: **любая правка конвертера — прогон по всем снапшотам, а не по свежему.** Проверка
одного файла маскирует регрессию на остальных (см. питфолл 72, §18.2).
### 🔴 Главное правило: `raw` не трогать
Часть полей декодирована (адрес, интервал, регистры, сценарии), часть лежит как `raw` / `raw_params` /
@@ -451,6 +480,8 @@ enabled = not (f5 & 8)
| ⛔ **`action: <id>` и вообще ключ `action`** | **ОТМЕНЁН** (круг 39, `8f2edd4`). При поднятом условии шага в `steps` нет вообще: id шага едет в `trigger.step_id`, `steps` = тела действий. См. §5.3, §21.3 |
| ⛔ **`args: [0, 1]` у `set_var` с числовым полем 2** | поле 5 записи 59 — **производное** («поле 2 = литерал, а не id»), энкодер ставит сам. Alex: «да блядь! опять?!» (§21.5) |
| ⛔ **раскрывать поля 4/5 записи 59 в YAML** | у `expr` поле 4 = `2` и у `set` поле 5 = `1`**одно и то же производное поле** («операнд/значение — литерал»). В YAML не пишется, восстановимо по составу (§21.5) |
| ⛔ **`params:` у шага-действия (запись типа 5)** | хвост `0,0,[],0,0,0` (поля 4..9) — дефолты, а поле 10 = `512`/`256`**код действия**, он и есть `value`. Alex: «тут просто выполнить действие по id». Шаг = `descr` + `target` + `value`, всё (§22, `9f3c1ab`) |
| ⛔ **`value: 1` как «значение действия»** | `1` — поле 3 (флаг формы), у всех 100+ действий одинаков. Настоящий код — в поле 10: `256` = выкл, `512` = вкл (§22) |
**РАЗРЕШЕНО:** `trigger:` (выводится из тела — один шаг 46) с ключом **`step_id`** (id шага 46) и
**телами действий прямо в `steps`**, `if`/`then`/`else`/`flag`**реальные поля записи 46**,
@@ -722,6 +753,9 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z<id>=<тип>'
| 28r | 🔴 **Операнды условия 47 не раскрывались → выглядели орфанами** | `dump_leaf` писал голый `left: 10088`, хотя `#Z10088=59,'objstate 9838 0 0'` в конфиге **есть**. Alex: «почему left: 10088 — в орфане?!». Правило: **голый id в YAML = красный флаг**, любой операнд раскрывается телом (`_operand_body` / `_operand_id`) |
| 28s | 🔴 **`unresolved: true` — шум, а не информация** | Для объекта, которого нет в конфиге (битая ссылка прибора: `10099`, `8601`, `10103`), достаточно **голого `id`** — энкодер соберёт `[10098,10099]` байт-в-байт и без отметки. Проверено на копии до правки. Alex: «але блядь!!!» трижды. Снято в 4 местах: `dump_step`, `dump_condition`, `dump_leaf`, список шагов |
| 28t | 🔴 **`value` у `var` — не значение, а поле 2** | `#Z8472=59,'set var1',0,0,0` → поле 2 = `0`. Раскрытие его как `value: 0` путалось с «значением переменной». Попытка переименовать в `from` — тоже отвергнута («я сказал снести, а не менять названия»). **Итог: поле 2 у `var` в YAML не раскрывается**, энкодер подставляет `0` |
| 28u | 🔴 **Хвост полей записи типа 5 сваливался в `params`, реальный код действия уезжал в конец списка** | `#Z9500=5,'Вкл. Рад. ванная 2эт',145728,1,0,0,[],0,0,0,512`: парсер внутри шага брал `value = step[3]` (поле 3 = **флаг**, всегда `1`) и сваливал поля 4..10 в `params`. Настоящий код действия — **поле 10** (`512`). Симптом в артефакте: `value: 1` + `params: [0,0,[],0,0,0,512]`. Alex: «это че за хуйня у нас зарегрессилась? тут просто выполнить действие по id». Фикс: `value = step[10]`, поля 3..9 не выводятся (дефолтные, восстанавливаются энкодером) |
| 28v | 🔴 **Энкодер типа 5 собирал 4 поля вместо 11** | Симметрично 28u: `fields = [5, descr, target, value]` + `params` — и при непустом `params` строка удлинялась сверх 11 полей. Правильная сборка: `[5, descr, target, 1, 0, 0, [], 0, 0, 0, value]`. Хвост `params` допустим **только** как продолжение (поля 11+), иначе `require(len(v) == 11)` в декодере упадёт |
| 28w | 🔴 **`params` как ключ для «ничего не значащего хвоста»** | Тот же класс, что `args`/`flag`/`kind`/`tail` (§28o): имя родилось из «осталось что-то после разобранных полей». Признак ошибки — **большинство дефолты**, значение только в одном-двух местах. Прежде чем давать ключ остатку — проверить, не является ли он **производным** от уже разобранных полей (ср. 28x). Легитимный `params` — общий список именованных параметров Modbus-устройств (`raw_params`, §1), не хвост записи |
### 7.3. Проверка гипотез
@@ -3093,13 +3127,18 @@ Alex прочитал `git show --stat ba6ef44` (2 файла, 10 строк) и
| | |
|---|---|
| **HEAD** | **`4b99d8e`** — «set_var: args убран у числового поля 2, поле 5 ставится само» · ✅ **ЗАПУШЕН** |
| **Рабочее дерево** | чистое по конвертерам |
| **Рабочее дерево** | ⚠️ **изменено, НЕ закоммичено** (`config-to-yml.py`, `yml-to-config.py` — круг 42, §22) |
| **Файл на диске** | `config_local_2026-09-17_22-26-26.yml` — 757 объектов; `trigger.step_id`, `end: true` у терминаторов, `args` только у `objcmd` |
| **Круг** | ✅ **9/9** — потеряно 0, лишнее 0 |
| **Круг** | ✅ **8/8**`zont_config/` 8 снапшотов `.txt`) — потеряно 0, лишнее 0 |
| **Коммиты сессии** | `4b99d8e` · `8f2edd4` · `272f4c6` · `6d0b6a5` · `4c6a6f5` · `806ed2e` · `b770bed` · `ba6ef44` · `0cbbeb8` · `2007771` · `0c4b9a6` · `821659b` · `16ec710` · `375d01a` · `07c076f` — ✅ **ЗАПУШЕНЫ** (`d94b809..4b99d8e`) |
| **Док в vault** | `personal/projects/zont-config-compiler.md` — коммит vault `ab32dd4` |
| **Незакоммичено в `zont_config/`** | 2 удалённых `.yml`-снапшота (`18-43-24`, `19-53-21`), новый `.txt` |
| **Открыто** | только поле 4 = `2` у `expr` (`10077``10085`) — имя не дано; всё остальное ✅ закрыто |
⚠️ **Первая строка «HEAD» описывает коммиты, а не рабочее дерево.** После круга 42 в дереве лежат
**несохранённые** правки `config-to-yml.py` / `yml-to-config.py` — при следующей сессии начинать с
`git status` / `git diff`, а не с предположения «всё закоммичено».
### 21.2.1 ✅ Круг 40 (ФИНАЛ) — `set_var`: `args` выдуман, поле 5 производное
**«да блядь! опять?!»** на `args: [0, 1]` у `#Z10069` — Alex отверг его в третий раз (круги 32,