From 041446b32c4a42b217f1da80d24046cd98ad0e5d Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 17 Sep 2026 20:53:38 +0600 Subject: [PATCH] [2026-09-17] eagle: personal/projects/zont-config-compiler.md --- personal/projects/zont-config-compiler.md | 96 ++++++++++++++++++++--- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/personal/projects/zont-config-compiler.md b/personal/projects/zont-config-compiler.md index 30756bc2..207b1727 100644 --- a/personal/projects/zont-config-compiler.md +++ b/personal/projects/zont-config-compiler.md @@ -3,7 +3,7 @@ title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml namespace: personal type: how-to created: '2026-09-17' -updated: '2026-09-17e' +updated: '2026-09-17f' tags: - personal - zont @@ -365,8 +365,8 @@ enabled = not (f5 & 8) | `level: I` / `level: A` (буква) | уровень пишется **словом** `info`/`alert` (§10.2 круг 1) | | `id` внутри `storeenv` | id команды уже снаружи, в ключе `id` объекта (§10.2 круг 3б) | | `set_var_name` / имя целевого объекта вместо id | **подстановка имени из другого объекта** — у ссылки только `id` (§5.10, §10.1) | -| `field` / `value` в теле объекта 49 | сырые имена без семантики → `mode` + `event`, §10.4 | -| `value: <код>` при `mode: event` | код события, а не имя; список Alex дал готовые имена, §10.4 | +| `field` / `mode` — отдельный ключ для поля 3 | поле 3 восстанавливается по составу ключей (`event` → `1`, `value` → `0`); Alex отверг оба имени, §10.1 круги 19–21 | +| `value: <код>` при событии | код события, а не имя; список Alex дал готовые имена, §10.4 | | `set_var: {n: 1, target: 8829}` | «номер» переменной я выдумал; имя и id — отдельные ключи `name`/`id`, §10.1 круг 12 | | `set_var: {var1: 8829}` (голый id) | значение обязано быть **развёрнуто** телом цели, а не отослано в orphans, §10.1 круг 13 | | `set_var: {var1: {id, …}}` — имя как ключ-контейнер | «var1 это имя переменной блядь» → `id`+`name` внутри `set_var`, §10.1 круг 15 | @@ -410,7 +410,10 @@ enabled = not (f5 & 8) | 15 | `set_var: {var1: {id, …}}` — имя переменной как ключ-контейнер | «var1 это имя переменной блядь» → `id`+`name` внутри, тело плоско (§10.1) | | 16 | `mask: 123` + `_head: [1,0,0]` у маски дней | «нормально блядь дни разверни и че за head блядь… как в schedule сделано, паттерн уже есть» → `days: [mon,…]` + `raw` (§10.1) | | 17 | `object_name: 'Контур газ котла'` рядом с `object: 8560` | **«откуда там блядь имя объекта?!»** → подстановка имени из чужого объекта запрещена, `object` = только id (§5.7) | -| 18 | `type: condition` / `type: days_mask` в теле цели | «че блядь за `type: condition`?» → тип не пишем, ветвимся по составу ключей (§10.1) | +| 18 | `type: condition` / `type: days_mask` в теле цели | «че блядь за `type: condition`?» → тип убран, ветвление по составу ключей | ✅ вернулся в круге 21 | +| 19 | `field: 1` (сырое имя поля 3) | «че блядь за `field/value`?» | ⛔ | +| 20 | `mode: compare\|event` — переименовал заодно с ответом про события | «какой нахуй `field`!» · «еб твою мать» | ⛔ | +| 21 | `type: condition` + `event`/`value`, поле 3 НЕ пишется | **«`type: condition` блядь!»** | ✅ **принято** | > 📌 Круги 10–11 разобраны в §10.2, 12–18 — в §10.1, 14 — в §10.3. Итог: `storeenv: {level: info|alert, text}` > — два ключа, `id` команды снаружи, словами, без `event`; `set_var: {id, name, object/operator/value @@ -566,6 +569,10 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z=<тип>' | 14 | ⚠️ Проверочные скрипты на кириллице падают на `cut`/`awk` | Читать в Python (`open(...,'rb').read().decode('cp1251')`), не резать шеллом | | 15 | 🔴 **Счёт ключей одним `grep '^ key:'` врёт** | Отступ разный (вложенный шаг +2 пробела), а часть объектов вообще в других секциях. Печатать **какие id** не попали, прежде чем выводить «N из M» | | 16 | 🔴 **`patch` по относительному пути попадает в проект, не в vault** | `patch(path='personal/...')` резолвится от CWD (`~/Automation/...`) → «file not found». Для `obsidian/` — **абсолютный** путь `/Users/admin/obsidian/personal/...` | +| 17 | 🔴 **Ключ «за компанию»: после «да» на ОДИН вопрос не переименовывать второй** | Alex сказал «да» про имена событий — я заодно переименовал `field` → `mode` → «**еб твою мать**». Потом три круга отката (`field` → без ключа → `type: condition`), Alex: «я тебе нахуя бл*ть чето пишу!». **Один ответ = одна правка.** | +| 18 | 🔴 **Alex называет ГОТОВОЕ имя, а не направление** | «`field` его обзови» / «`type: condition` блядь!» — это финальный ответ, а не подсказка для дальнейшего изобретения. Принимать буквально, не искать «улучшение». Он отвергает промежуточные формы (круги 19–21: `field`/`value` → `field` → `type: condition`). | +| 19 | 🔴 **Не «узнавать» смысл поля по аналогии с другим типом** | Объект 50 дважды выведен неверно: как расписание сценария (`days`) и как маска. Alex: «какой нахуй days! я тебе блядь недоступно написал?!» → истина «current time > 13:23». **Сначала СЛОВА из UI, потом модель** (питфолл 32). Проверять множество значений поля по ВСЕМУ конфигу (`Counter`), не строить аналогию. | +| 20 | 🔴 **Выдуманный термин = выдуманная модель** | Alex: «че такое `cmp`… гдето у нас еще есть термин "cmp"?» — термина в проекте не было. Имена ключей брать из **уже принятого словаря** (`op` у типа 47), не изобретать синонимы. | ### 7.2. Код — парсер/энкодер @@ -590,6 +597,8 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z=<тип>' | 28a | 🔴 **10 разных `set var1` выглядят в YAML одинаково** | `descr` у всех один, различие — в `set_var` (+ `args[0]`). Не «оптимизировать» их обратно в одну запись. §5.10, §10.1 | | 28b | ⚠️ **Несущие объекты 49/50 живут в `scenario_orphans`, не в теле шага** | `#Z8830` → `#Z8829=49,8450,1,0`. Sweep (§7.4) докидывает их только как raw-склад. Раскрытие = отдельное решение (вариант B §10.1), не «попутная починка» | | 28c | 🔴 **Две точки входа энкодера: правка в одной = потеря правки при зелёном round-trip** | `emit_action` (скрипты из списков/`scenario_orphans`) и инлайн-ветка `emit_step` (`descr`+`args`) — **форк**. Новый ключ вводить в **обе**; проверять тестом на **подмену значения** (§10.1) | +| 28d | 🔴 **Регексп тела 59 был `^set var(\d+)$` → пропускал `set varname`** | Имя переменной — произвольный идентификатор, в UI задаётся руками. ✅ `^set (var\w*|\w+)\s*$`. Симптом: `descr: set varname` вместо `set_var` (§10.4) | +| 28e | 🔴 **Секции YAML не несут номер типа объекта** | Энкодеру для разворота события нужна карта `id → тип`. `_body_type()`: словарь секций → тип + `executors.relays`/`analog_outputs` + `raw`-склад (тело = `[тип, …]`). Без карты — `exit 2`: `unknown event 'lost' for object 8450 (type None)` (§10.4) | ### 7.3. Проверка гипотез @@ -686,11 +695,14 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO____.zip | Что | Состояние | |---|---| -| Round-trip | 🟢 **ЗЕЛЁНЫЙ, байт-в-байт** — `661 → 661`, `686 → 686`, `diff` = 0 (проверен на `19-53-21`) | +| Round-trip | 🟢 **ЗЕЛЁНЫЙ, байт-в-байт** — `698 → 698`, `723 → 723` на снимке `20-40-48` (было `661 → 661` на `19-53-21`) | | Форма сценария | ✅ закрыта (§5), оба конвертера переведены | -| Тела типа 59 | ✅ **`storeenv` (§10.2), `set_var` с вложенным телом цели (§10.1, круги 12–18), `log` плоский (§10.3) — готовы, не закоммичены** · `objcmd`/`expr`/`objstate` — `descr`+`args` | +| Тела типа 59 | ✅ **`storeenv` (§10.2), `set_var` с вложенным телом цели (§10.1, круги 12–21), `log` плоский (§10.3) — готовы, не закоммичены** · `objcmd`/`expr`/`objstate` — `descr`+`args` | +| Условия 49 (`set_var`) | ✅ **форма закрыта** — `type: condition` + `object` + `event`\|`value`; поле 3 в YAML не пишется; 18 комбинаций из **Conditions Test** (§10.4) | +| Имя переменной | ✅ `set var1` **и** `set varname` — регексп `^set (var\w*|\w+)$` (§10.4) | +| `field` / `mode` | ⛔ **удалены** — 0 вхождений в файле (§10.1 круги 19–21) | | Потери объектов | ✅ **0** — было 5 (`8472`, `8821`, `8849`, `8851`, `8855`) | -| `unresolved: true` | ⚠️ висит у `#Z8860`, `#Z8864`, `#Z8601` — объекта в конфиге нет (§10) | +| `unresolved: true` | ⚠️ висит у `#Z8860`, `#Z8864`, `#Z8601` — объекта в конфиге нет (§10, §10.4) | | Коммит | `b75c51f` — «Drop stale YAML snapshots in old scenario shape» ← **текущий HEAD** | | Откачено | `87e315c` («set-var target into `set_var`») — снят `git reset --soft` без разрешения Alex (§10.1) | | Ранее | `823fabd` — форма сценария §5; `1cc010a` — **содержит сломанные версии**; закрыт §9.1 | @@ -869,6 +881,22 @@ curl -s --max-time 20 http://192.168.0.50/config.txt -o "zont_config/config_loca > ❌ **`8860` в Conditions Test НЕ появился** — опровергает версию «8860 = один из типовых conditions». +#### 🔴 Имя переменной — не только цифры (`set varname`) + +Регексп тела `set_var` был `^set var(\d+)$` и **пропускал `set varname`** — такие шаги уходили +в `descr`+`args`. Alex: «че за хуйня то блядь опять?! … `descr: set varname`». + +```python +# ✅ принимает и 'set var1', и 'set varname' +m_sv = re.match(r'^set (var\w*|\w+)\s*$', code) +``` + +Имя переменной в конфиге — **произвольный идентификатор**, в UI задаётся руками (`varname`, `var1`). +Не привязываться к «var + цифры». + +> ⚠️ **Исключение:** `#Z8472=59,'set var1',0,0,0` — цель `0`. Такие записи остаются `descr`+`args` +> (разворачивать нечего, `set_var` без цели не собирается). Это **не** потеря: round-trip зелёный. + #### Энкодер: тип целевого объекта ищется по секциям (`_body_type`) Секции YAML не несут номер типа объекта, поэтому энкодеру нужна карта `id → тип`: @@ -974,6 +1002,9 @@ Alex видел `field: 1` **после** переименования в `mode` raw: [1, 0, 0] # ← поля 2..4 строки 50 ``` +> ⛔ **ОТМЕНЕНО (круг 22): объект 50 — это НЕ «маска дней».** Разбор — §10.5. +> Alex: «там: current time ">" 13:23» и «какой нахуй days». Форма выше **устарела**. + 🔴 **`type` в `set_var` НЕТ** (круг 17). Тело различается по составу ключей: `days`/`days_mask` → объект 50; `object` + `mode` → объект 49. Энкодер ветвится так же (`tgt_type == 'days_mask'` / `('object' in sv)`). Неизвестное тело — `raw`. @@ -1209,17 +1240,60 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z8830=59' > (вложенный шаг), а два — в `scenario_orphans`. Реальный счёт был 3 из 5, не 2. Считать без > привязки к отступу и печатать **какие именно id** не попали, прежде чем делать вывод. +### 10.5. ✅ Объект 50 — условие по ВРЕМЕНИ, не «маска дней» (круг 22, 2026-09-17) + +**Отправная точка:** `#Z8842=50,0,1,3351,0` выводился как `type: days_mask` + `days: []` + +`days_mask: 0` + `raw: [0, 1, 3351]`. Alex: **«откуда блядь опять raw выполз блядь»**, затем +**«там: current time ">" 13:23»**, затем **«какой нахуй days! я тебе блядь недоступно написал?!»**. + +**Итог:** объект 50 — это условие `current time HH:MM`. `3351 = 13:23` — это **время**, +тот же формат, что у расписания сценария (`time = (час << 8) | мин`, §5.6; ср. `#Z8597` = `3354` = 13:22). + +```yaml +- id: 8843 # #Z8843=59,'set var1',8842,0,0 + set_var: + id: 8842 # #Z8842=50,0,1,3351,0 + name: var1 + type: time_condition + op: '>' # поле 3 = 1 → '>' (LEAF_OPS) + time: '13:23' # поле 4 = 3351 +``` + +| Поле 50 | Что | В YAML | +|---|---|---| +| 2 | неизвестно (`0` у `8842`, `1` у `8844`/`8548`) | `_f2` — **только если ≠ 0** | +| 3 | **оператор** сравнения (`LEAF_OPS`) | `op: '>'` | +| 4 | **время**, `(час<<8)\|мин` | `time: 'HH:MM'` | +| 5 | неизвестно (`0` / `123` / `109`) | `_f5` — **только если ≠ 0** | + +> ⛔ **Запрещено (круг 22):** `days`, `days_mask`, `mask`, `raw` в теле объекта 50 — Alex отверг +> все четыре подряд («какой нахуй days», «какой mask», «откуда raw выполз»). Поля 2/5 — +> служебные `_f2`/`_f5` и **не пишутся, когда нули**. + +> 🔴 **Ключ знака — `op`, не `cmp`.** Alex: «че такое cmp… гдето у нас еще есть термин "cmp"?» — +> термина в проекте не было, я его выдумал. У типа 47 (§5.2) тот же смысл называется `op`, +> берём единообразно. `LEAF_OPS` = `{0:'<',1:'>',2:'=',3:'<=',4:'>='}`. + +**Проверка §10.5:** round-trip `20-40-48` → ✅ `698 → 698`, `723 → 723`; `cmp` в файле — **0 вхождений**. +Тест на подмену: `op: >` → `<=`, `time: 13:23` → `07:45` даёт `#Z8842=50,0,3,1837,0` +(`3` = `<=`, `1837 = 7*256+45`). + +> ⚠️ **Паттерн ошибки (круги 15 → 22):** я дважды «узнавал» смысл объекта 50 по аналогии — +> сначала как расписание сценария (`days`), потом как маску. Alex оба раза бил: **сначала СЛОВА +> из UI, потом модель** (питфолл 32). Для объекта 50 слова даны: «current time > 13:23». + --- ## 11. Файлы проекта | Файл | Статус | |---|---| -| `config-to-yml.py` | ✅ форма §5: `trigger:` подъём через `pop('if')`, шаг = `{id, [flag], action}` или `{id, [flag], if, then, [else]}` · ✅ тип 59: `set_var` (§10.1) и `storeenv` (§10.2) — **в рабочей копии, не закоммичено** | -| `yml-to-config.py` | ✅ `emit_step` читает `then`/`action`/`else`/`flag`; `f5` из `trigger:`/`interval_ms` + бит 8 · ✅ `set_var`/`storeenv` собраны в **`_script_body()`** — одна функция на обе точки входа (`emit_action` + инлайн `emit_step`) — **не закоммичено** | +| `config-to-yml.py` | ✅ форма §5: `trigger:` подъём через `pop('if')`, шаг = `{id, [flag], action}` или `{id, [flag], if, then, [else]}` · ✅ тип 59: `set_var` (§10.1), `storeenv` (§10.2), `log` (§10.3), `type: condition` + `event`/`value` (§10.4), `type: time_condition` (§10.5) — **в рабочей копии, не закоммичено** | +| `yml-to-config.py` | ✅ `emit_step` читает `then`/`action`/`else`/`flag`; `f5` из `trigger:`/`interval_ms` + бит 8 · ✅ `set_var`/`storeenv` собраны в **`_script_body()`** — одна функция на обе точки входа (`emit_action` + инлайн `emit_step`) · ✅ `_body_type()` — карта `id → тип` по секциям + `raw`-склад (§10.4) — **не закоммичено** | | `test_roundtrip.py` | ✅ без изменений (в коммите `199f2b1`) | -| `zont_config/config_local_2026-09-17_19-53-21.{txt,yml}` | 🆕 **актуальный** снимок с прибора (686 строк, 661 `#Z`) — в индексе, **не закоммичен**; YAML перегенерирован (форма §10.2, 4× `storeenv`) | -| `zont_config/config_local_2026-09-17_18-43-24.{txt,yml}` | предыдущий снимок, форма §5 | +| `zont_config/config_local_2026-09-17_20-40-48.{txt,yml}` | 🆕 **АКТУАЛЬНЫЙ** снимок (723 строки, 698 `#Z`) — содержит сценарий **`9144` Conditions Test** (18 шагов, все комбинации условий). Круг ✅ `698 → 698`. **Не закоммичен** | +| `zont_config/config_local_2026-09-17_19-53-21.{txt,yml}` | предыдущий снимок (686 строк, 661 `#Z`) — форма §10.2/§10.3; **форма объекта 50 в нём УСТАРЕЛА** (§10.5) | +| `zont_config/config_local_2026-09-17_18-43-24.{txt,yml}` | ещё раньше, форма §5 | | `zont_config/config_local_2026-09-17_{17-45-00,16-13-28,16-02-18,14-16-35}.*` | исторические снапшоты | | `zont_config/config_0FA7C33CC89F_…_12-12-28.txt` | боевой конфиг (598 `#Z`, 25 `#S`) | | `zont_config/archive/` | `-2`/`-3`/`-4` — закоммичены (`7ae0e32`) |