From dafdcf9ec6a59e089e211457ee623fceaf55541e Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 17 Sep 2026 20:58:43 +0600 Subject: [PATCH] [2026-09-17] eagle: personal/projects/zont-config-compiler.md --- personal/projects/zont-config-compiler.md | 143 +++++++++++++++++----- 1 file changed, 110 insertions(+), 33 deletions(-) diff --git a/personal/projects/zont-config-compiler.md b/personal/projects/zont-config-compiler.md index 207b1727..2a84fe35 100644 --- a/personal/projects/zont-config-compiler.md +++ b/personal/projects/zont-config-compiler.md @@ -378,6 +378,9 @@ enabled = not (f5 & 8) | `log: {text: …}` | ключ-контейнер на один аргумент → плоский `log: <текст>`, §10.3 | | ⛔ **изобретать форму для смысла, у которого уже есть паттерн** | «как в schedule сделано! паттерн уже есть» — сперва `grep` принятую форму (дни, время, интервал), §10.1 круг 16 | | ⛔ **переносить константы одного типа на другой по имени поля** | `LEAF_OPS` типа 47 применён к объекту 49 — множество значений поля по данным не проверено, §10.1 круг 18 | +| ⛔ **применять разобранную форму ко ВСЕМ объектам типа, а не только к подтверждённым** | временную форму 50 применил к `8844` → `op: <` / `time: 00:00`; Alex: «ты сломал его нахуй», §10.5 круг 24 | +| ⛔ **`days_mask: <число>` / `mask: <число>` / `_head` / `raw` в теле объекта 50** | «какой нахуй days», «какой mask», «откуда блядь опять raw выполз», «че за head блядь» — §10.5 | +| ⛔ **выдумывать знак сравнения, если поле не подтверждено** | `>` у `8842` получен случайным совпадением (`1` ∈ `LEAF_OPS`); контрпримера нет — знак в YAML не пишется, §10.5 | ✅ **РАЗРЕШЕНО:** `trigger:` (выводится из тела — один шаг 46), `if`/`then`/`else`/`flag` — **реальные поля записи 46**, `action` — имя списка у шага с поднятым условием, `set_var: {id, name, object/operator/value @@ -389,7 +392,7 @@ enabled = not (f5 & 8) > 📌 **Общий принцип:** YAML-ключ обязан соответствовать полю строки конфига либо выводиться из тела. > **Подстановка из другого объекта запрещена.** -### 5.8. 🔴 История формы — 18 кругов (не повторять) +### 5.8. 🔴 История формы — 24 круга (не повторять) | Круг | Что затащил | Реплика Alex | |---|---|---| @@ -414,10 +417,14 @@ enabled = not (f5 & 8) | 19 | `field: 1` (сырое имя поля 3) | «че блядь за `field/value`?» | ⛔ | | 20 | `mode: compare\|event` — переименовал заодно с ответом про события | «какой нахуй `field`!» · «еб твою мать» | ⛔ | | 21 | `type: condition` + `event`/`value`, поле 3 НЕ пишется | **«`type: condition` блядь!»** | ✅ **принято** | +| 22 | объект 50 разложен как расписание: `days`/`days_mask`/`mask`/`raw`/`_head` | «откуда блядь опять raw выполз» · «какой нахуй days» · «какой mask» | ⛔ §10.5 | +| 23 | `cmp` вместо `op` (термин выдуман) | «че такое `cmp`… гдето у нас еще есть термин cmp?» | ⛔ → `op`, единообразно с типом 47 | +| 24 | временную форму 50 применил ко ВСЕМ объектам 50 (`8844` → `op: <`/`time: 00:00`) | «ты сломал его нахуй» · «ты дебил?!» | ⛔ → две формы по полю 3, §10.5 | -> 📌 Круги 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 -> либо days/days_mask}` — плоско, без `type`; `log: <текст>` — плоская строка. +> 📌 Круги 10–11 разобраны в §10.2, 12–18 — в §10.1, 14 — в §10.3, 19–24 — в §10.4/§10.5. +> Итог: `storeenv: {level: info|alert, text}` — два ключа, `id` команды снаружи, словами, без `event`; +> `set_var: {id, name, type, object/event|value}` — плоско; `log: <текст>` — плоская строка; +> объект 50 — две формы (`time_condition` / `days_mask`). > 🔴 **Три системных корня кругов 12–18:** > **(а) ключ-контейнер на один смысл** — `{var1: …}`, `{text: …}`: обёртка ради обёртки, Alex видит @@ -695,11 +702,12 @@ https://lk.zont-online.ru/download/firmwares/H2000_PRO____.zip | Что | Состояние | |---|---| -| Round-trip | 🟢 **ЗЕЛЁНЫЙ, байт-в-байт** — `698 → 698`, `723 → 723` на снимке `20-40-48` (было `661 → 661` на `19-53-21`) | +| Round-trip | 🟢 **ЗЕЛЁНЫЙ, байт-в-байт** — `698 → 698`, `723 → 723` (снимок `20-40-48`); ранее `661 → 661` (`19-53-21`). Прогон на `20-55-22` — **не сделан** (снимок только снят, конвертеры не менялись) | | Форма сценария | ✅ закрыта (§5), оба конвертера переведены | | Тела типа 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) | +| Объект 50 (`set_var`) | ✅ **две формы по ПОЛЮ 2**: `0` = время → `type: time_condition` + `time`; `1` = дни → `type: days_mask` + `days` (§10.5). ⚠️ **знак сравнения лежит в ПОЛЕ 3** (найден круг 25: `9248`=`1`, `9250`=`0`, `9252`=`2`, `9254`=`4`) — коды совпадают с `LEAF_OPS` типа 47, но **`op` в YAML не пишется, пока Alex не назвал подписи этих 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, §10.4) | @@ -770,9 +778,9 @@ git commit -m "Scenario YAML shape: bare action ids, no anchors" ⚠️ **Остаётся `raw` / `unresolved`:** -1. **Тип 50** — маска дней недели: `#Z8548=50,1,0,0,109`, где `109 = 0b1101101` = пн, ср, чт, сб, вс. - Alex: «это выбор дней недели, то же самое что ставится в значение var1». ✅ **Маска локализована: - ПОЛЕ 5** (сверено по `8548`). Вне `set_var` объекты 50 по-прежнему `raw`. +1. **Тип 50** — ✅ **РАЗОБРАН** (§10.5, круг 25): поле 2 = признак формы (`0` время / `1` дни), + поле 3 = **знак сравнения**, поле 4 = время, поле 5 = маска дней. Осталось **одно**: подписи + 4 условий Alex (`9248`/`9250`/`9252`/`9254`) из UI → тогда `op` пишется. 2. ~~**Несущие объекты `set var1`** — тело цели в `scenario_orphans`~~ → ✅ **ЗАКРЫТО**, §10.1 круг 13: тело цели разворачивается **внутрь** `set_var`. **Вариант B выбран Alex.** 3. **`unresolved: true`** у `#Z8860`, `#Z8864`, `#Z8601` — этих объектов нет в конфиге. @@ -1240,47 +1248,115 @@ 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) +### 10.5. ✅ Объект 50 — ДВЕ формы: время и дни (круги 22–24, 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! я тебе блядь недоступно написал?!»**. +`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). +**Итог:** объект 50 имеет **ДВЕ формы**, различаются **полем 3**: + +| Поле 3 | Форма | Поле 4 | Поле 5 | YAML | +|---|---|---|---|---| +| `1` | условие по времени | **время** `(час<<8)\|мин` | 0 | `type: time_condition` + `time: 'HH:MM'` | +| `0` | **выбор дней недели** | 0 | **маска дней** (бит 0 = ПН) | `type: days_mask` + `days: [mon, …]` | ```yaml - id: 8843 # #Z8843=59,'set var1',8842,0,0 set_var: - id: 8842 # #Z8842=50,0,1,3351,0 + id: 8842 # #Z8842=50,0,1,3351,0 (поле 3 = 1 → время) name: var1 type: time_condition - op: '>' # поле 3 = 1 → '>' (LEAF_OPS) - time: '13:23' # поле 4 = 3351 + time: '13:23' # поле 4 = 3351 = 13*256+23 + +- id: 8845 # #Z8845=59,'set var1',8844,0,0 + set_var: + id: 8844 # #Z8844=50,1,0,0,123 (поле 3 = 0 → дни) + name: var1 + type: days_mask + days: [mon, tue, thu, fri, sat, sun] # поле 5 = 123 ``` | Поле 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** | +| `2` | **признак формы**: `1` = **дни недели**, `0` = **время** (подтверждено, круг 25) | **не пишется** — восстанавливается из `type` | +| `3` | **знак сравнения** (`0` `<` · `1` `>` · `2` `=` · `4` `>=` …) или `0` при форме «дни» | `op: '…'` (нужна расшифровка всех кодов в UI) | +| `4` | при форме «время» — **время** `(час<<8)\|мин`; при «дни» — `0` | `time: 'HH:MM'` | +| `5` | при форме «время» — `0`; при «дни» — **маска дней** (бит 0 = ПН) | `days: [mon, …]` | -> ⛔ **Запрещено (круг 22):** `days`, `days_mask`, `mask`, `raw` в теле объекта 50 — Alex отверг -> все четыре подряд («какой нахуй days», «какой mask», «откуда raw выполз»). Поля 2/5 — -> служебные `_f2`/`_f5` и **не пишутся, когда нули**. +#### 🔴 Круг 25: тостинг Alex — знак сравнения НАЙДЕН (поле 3) -> 🔴 **Ключ знака — `op`, не `cmp`.** Alex: «че такое cmp… гдето у нас еще есть термин "cmp"?» — +**Как добыто:** Alex в UI переключил `8842` на другие знаки, добавил 4 разных time-условия +и снял конфиг: + +```bash +cd /Users/admin/Automation/HA-ZONT-Modbus/zont_config +TS=$(date +%Y-%m-%d_%H-%M-%S) +curl -s --max-time 25 http://192.168.0.50/config.txt -o "config_local_${TS}.txt" +# → config_local_2026-09-17_20-55-22.txt, 729 строк +``` + +**Пять объектов 50 в новом снимке** (`#Z8548` + 4 новых тостинга + `#Z9256`): + +| #Z | строка конфига | поле 2 | поле 3 | поле 4 | поле 5 | форма | +|---|---|---|---|---|---|---| +| 9248 | `50,0,1,3351,0` | `0` | **`1`** | 3351 = **13:23** | 0 | время | +| 9250 | `50,0,0,2817,0` | `0` | **`0`** | 2817 = **11:01** | 0 | время | +| 9252 | `50,0,2,3842,0` | `0` | **`2`** | 3842 = **15:02** | 0 | время | +| 9254 | `50,0,4,3847,0` | `0` | **`4`** | 3847 = **15:07** | 0 | время | +| 8548 | `50,1,0,0,109` | **`1`** | `0` | 0 | 109 | дни | +| 9256 | `50,1,0,0,123` | **`1`** | `0` | 0 | 123 | дни | + +**Вывод:** у объектов 50 поле 2 — **признак формы** (`0` = время, `1` = дни), поле 3 — **знак сравнения** +(у формы «время»; у формы «дни» всегда `0`), поле 4 — время, поле 5 — маска дней. + +🟢 **Коды знаков совпадают с `LEAF_OPS` типа 47 по позиции** (`1` = `>`, `0` = `=`, `2` = `<`, +`4` = `>=` или `<=`). ⚠️ **Но это по-прежнему гипотеза** — Alex тостинг сделал, а **подписи из UI +не назвал**. Пока подписи нет, `op` в YAML **не пишется** (питфолл: не выводить по совпадению без контроля). + +**Что нужно, чтобы закрыть:** Alex называет подписи 4 условий (`9248`, `9250`, `9252`, `9254`) — +либо одно слово «прими по позиции `0=<, 1=>, 2===, 3=<=, 4=>=`». + +**Как связывать шаги:** шаги-держатели `set var1` для новых условий: +`#Z9249=59,'set var1',9248,0,0` · `9251`→`9250` · `9253`→`9252` · `9255`→`9254` · `9257`→`9256`. + +> ⚠️ **Питфолл разбора: нумерация полей у объекта 50 — с типа, а не с первого числа после `=`** +> (круг 25). `#Z9248=50,0,1,3351,0` → `поле2=0` (не `1`!), `поле3=1`, `поле4=3351`, `поле5=0`. +> Я сначала посчитал `cut -d, -f5` → получил `0` и «время 00:00» — сдвиг на единицу. +> 🔴 **Резать поля от `type`:** `fields[0]=type`, `fields[1]=поле2`, … Проверять `grep -oE` со +> **скобочной группой до конца строки** `[...](?=\r|\n)`, иначе `[0-9,\-]*` съедает не то. + +Все три объекта 50 живого конфига: `#Z8548=50,1,0,0,109` (дни), `#Z8842=50,0,1,3351,0` (время), +`#Z8844=50,1,0,0,123` (дни). `109` = пн,ср,чт,сб,вс; `123` = пн,вт,чт,пт,сб,вс. + +> ⛔ **Запрещено (круг 22):** ключ `days_mask` как число, `mask`, `raw`, `_head` в теле объекта 50. +> Alex отверг подряд: «какой нахуй days», «какой mask», «откуда raw выполз», «че за head блядь». +> Поля 2/3 **не пишутся** — восстанавливаются из `type` (`time_condition`/`days_mask`) и `op`. + +> ⛔ **Знак сравнения (`op`) в YAML НЕ пишется, пока Alex не назвал подписи.** Место кодирования +> **найдено** (поле 3, круг 25): `9248`=`1`, `9250`=`0`, `9252`=`2`, `9254`=`4`. Коды **совпадают +> с `LEAF_OPS` типа 47 по позиции**, но совпадение не подтверждено подписью из UI — выводить нельзя +> (питфолл: «гипотезу прогнать по ВСЕМ объектам и печатать расхождения ПРЕЖДЕ вывода»). +> **Чтобы закрыть: Alex называет, что за условия `9248`/`9250`/`9252`/`9254` в UI.** + +> 🔴 **Ключ знака — `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`). +(`3` = `<=`, `1837 = 7*256+45`). `days` у `8844` подменены (+`wed`) → `#Z8844=50,1,0,0,127`. -> ⚠️ **Паттерн ошибки (круги 15 → 22):** я дважды «узнавал» смысл объекта 50 по аналогии — -> сначала как расписание сценария (`days`), потом как маску. Alex оба раза бил: **сначала СЛОВА -> из UI, потом модель** (питфолл 32). Для объекта 50 слова даны: «current time > 13:23». +> ⚠️ **Паттерн ошибки (круги 15 → 22 → 24):** я трижды «узнавал» смысл объекта 50 по аналогии — +> сначала как расписание сценария (`days`), потом как маску, потом применил **временную** форму +> ко **всем** объектам 50 (получил `op: <` / `time: 00:00` у `8844` — Alex: «ты сломал его нахуй»). +> Alex объяснял только `8842`. **Правило:** разобранную семантику применять ТОЛЬКО к объектам, +> на которых она подтверждена; для остальных — проверять, что поле-различитель совпадает. + +> 🔴 **Питфолл: падение конвертера затирает целевой файл в 0 байт.** +> `python3 config-to-yml.py X.txt > X.yml` открывает `X.yml` **до** старта питона. Если скрипт падает +> (у меня — `NameError: op_raw`), на диске остаётся **пустой** файл, и Alex видит «нихуя не поменялось». +> Порядок: генерировать в `/tmp`, проверять `wc -c` и `grep`, и только потом `cp` в целевой путь. --- @@ -1288,11 +1364,12 @@ iconv -f cp1251 -t utf-8 out.txt | tr -d '\r' | grep '^#Z8830=59' | Файл | Статус | |---|---| -| `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) — **не закоммичено** | +| `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), объект 50 двумя формами — `time_condition`/`days_mask` (§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) · ✅ `days_mask`/`time_condition` (§10.5) — **не закоммичено** | | `test_roundtrip.py` | ✅ без изменений (в коммите `199f2b1`) | -| `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_20-55-22.{txt,yml}` | 🆕 **АКТУАЛЬНЫЙ** снимок (729 строк, 36204 байта) — добавлены 4 time-условия Alex (`#Z9248/9250/9252/9254`), знак сравнения локализован в поле 3 (§10.5 круг 25). **Не закоммичен** | +| `zont_config/config_local_2026-09-17_20-40-48.{txt,yml}` | предыдущий (723 строки, 698 `#Z`) — сценарий **`9144` Conditions Test** (18 шагов, все комбинации условий 49). Круг ✅ `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`) |