[2026-09-17] eagle: family/how-to/zont-config-compiler.md family/tech/zont-config-object-types.md
This commit is contained in:
@@ -21,7 +21,7 @@ tags:
|
|||||||
- homeautomation
|
- homeautomation
|
||||||
title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml
|
title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml
|
||||||
type: how-to
|
type: how-to
|
||||||
updated: 2026-09-17d
|
updated: 2026-09-17f
|
||||||
---
|
---
|
||||||
|
|
||||||
# ⚙️ ZONT Config Compiler — конвертеры `.txt ⇄ .yml`
|
# ⚙️ ZONT Config Compiler — конвертеры `.txt ⇄ .yml`
|
||||||
@@ -359,6 +359,10 @@ diff A.txt B.txt # пусто = round-trip чистый
|
|||||||
| 55 | 🔴 **Переименование служебного ключа не решает проблему** | `_f5` → `field5` («вербатим, не служебный») Alex отверг так же: «какой нахуй field5 ЕБАТЬ ТЕБЯ В СРАКУ?! МЫ НАХУЯ ЕГО СНОСИЛИ!!!». Если поле не является полем строки конфига — его нельзя ни прятать под `_`, ни выносить в открытый ключ |
|
| 55 | 🔴 **Переименование служебного ключа не решает проблему** | `_f5` → `field5` («вербатим, не служебный») Alex отверг так же: «какой нахуй field5 ЕБАТЬ ТЕБЯ В СРАКУ?! МЫ НАХУЯ ЕГО СНОСИЛИ!!!». Если поле не является полем строки конфига — его нельзя ни прятать под `_`, ни выносить в открытый ключ |
|
||||||
| 56 | 🔴 **Не докладывать баг по выводу `awk`/`grep`, не проверив вторым инструментом** | `awk '/^- id: 9691$/,/^- id: /'` вернул одну строку → я объявил «сценарий пустой, уехал в `orphans`». Это ошибка диапазона `awk` (диапазон закрылся на следующем `- id:` через строку), а не факт файла. Сценарий был полный |
|
| 56 | 🔴 **Не докладывать баг по выводу `awk`/`grep`, не проверив вторым инструментом** | `awk '/^- id: 9691$/,/^- id: /'` вернул одну строку → я объявил «сценарий пустой, уехал в `orphans`». Это ошибка диапазона `awk` (диапазон закрылся на следующем `- id:` через строку), а не факт файла. Сценарий был полный |
|
||||||
| 57 | 🔴 **Отсутствие поля в конфиге ≠ поле можно подставлять/переименовывать** | Поле 1 записи 46 (`#Z8862=46,1,…`) встречается 1 раз из 69. Соблазн: счесть константой `0` и не хранить. Правильно: сохранить как `flag` (имя по позиции), семантику не выдумывать. То же — `days_mask` для типа 50 |
|
| 57 | 🔴 **Отсутствие поля в конфиге ≠ поле можно подставлять/переименовывать** | Поле 1 записи 46 (`#Z8862=46,1,…`) встречается 1 раз из 69. Соблазн: счесть константой `0` и не хранить. Правильно: сохранить как `flag` (имя по позиции), семантику не выдумывать. То же — `days_mask` для типа 50 |
|
||||||
|
| 58 | 🔴🔴 **Проверять в ЦЕЛЕВОЙ файл, а не в `/tmp`** | Гонял `config-to-yml.py … > /tmp/new*.yml`, а снапшот на диске остался старым → Alex увидел `type: trigger` в `18-43-24.yml` и справедливо спросил «я чето не понял?». **Проверка в `/tmp` не проверяет артефакт.** Писать сразу в целевой путь, отдельно сверять его глазами |
|
||||||
|
| 59 | 🔴🔴 **`emit_step` имел ДВЕ ветки для 46 — старая перехватывала новую** | `emit_step:490` (`if 'if' in step:`) стоял **выше** нового (`if 'if' in step or 'then' in step:`) и читал удалённые `action`/`_else`/`_kind` → 12 объектов из `then` не регистрировались. Симптом: «потеряно 146, добавлено 134». **Перед отладкой логики искать дубли-ветки того же условия** (`grep -n "if '\w*' in step"`) |
|
||||||
|
| 60 | 🔴 **`emit_action` для вложенного 46 возвращал id без регистрации тела** | `if 'action' in node: return aid` — остаток от формы с поднятым триггером. Тела пауз `11824`/`11825`/`11826` пропадали, `#Z11827` выходил как `[46,0,11823,[],[]]`. Удалён; вместо него `emit_step(node)` |
|
||||||
|
| 61 | 🔴 **Тело сценария не определяет тип — определяет ФОРМА `steps`** | Круг 5: после вырезания `trigger:` я выводил тип из «есть шаг 46» → 2 расхождения. Признак — **`steps` = ровно один элемент, и он 46**. Alex сказал прямо: «наличием поля `trigger:`». **Когда Alex называет поле — это ответ, а не повод искать обходной путь** |
|
||||||
|
|
||||||
### Ограничения конвертера (найдено 2026-09-17) — ВСЕ ЗАКРЫТЫ
|
### Ограничения конвертера (найдено 2026-09-17) — ВСЕ ЗАКРЫТЫ
|
||||||
|
|
||||||
@@ -422,15 +426,18 @@ diff A.txt B.txt # пусто = round-trip чистый
|
|||||||
|
|
||||||
## 5c. 🔄 Переработка структуры YAML сценариев — ✅ СДЕЛАНО 2026-09-17
|
## 5c. 🔄 Переработка структуры YAML сценариев — ✅ СДЕЛАНО 2026-09-17
|
||||||
|
|
||||||
> ✅ **СТАТУС (2026-09-17, после правок формы): парсер дописан, YAML перегенерирован.**
|
> ✅ **СТАТУС (2026-09-17, ФИНАЛ): форма закрыта, round-trip байт-в-байт чистый.**
|
||||||
> 🟢 **Round-trip зелёный** по множеству строк: `661 → 661` объектов, `686 → 686` строк.
|
> 🟢 `661 → 661` объектов, `686 → 686` строк, `diff` по `#Z`-строкам = **0**.
|
||||||
>
|
>
|
||||||
> 🔴 **ВАЖНО — форма §5c/§5k НЕ является финальной.** В конце сессии Alex вырезал из неё
|
> ✅ **Актуальная форма — §6a.** Сценарий = `id`/`name`/`enabled` + опциональный `trigger:` +
|
||||||
> `type`, `field5` и `trigger` на уровне сценария. **Актуальная форма — §6a.**
|
> `steps[]`. Шаг 46 = `{id, [flag], if, then[], [else[]]}`.
|
||||||
> История отвергнутых решений — §6c (4 круга).
|
> **`type`, `field5`, `kind`, `_f5`, `_kind`, `_else`, `action`, `base5` — вырезаны из обоих
|
||||||
|
> конвертеров.**
|
||||||
>
|
>
|
||||||
> Открытый вопрос, блокирующий правку: **чем в YAML отличать `manual` от `trigger`**, если
|
> ✅ **Открытый вопрос `manual` vs `trigger` ЗАКРЫТ:** признак — **наличие `trigger:`** у сценария;
|
||||||
> ни `type`, ни `field5` писать нельзя. Факты — §6b, разбор — §6.
|
> он выводится из тела (условие поднимается, если `steps` = ровно один шаг 46). Alex:
|
||||||
|
> «наличием поля trigger:». Разбор — §6, правило — §6a, факты — §6b.
|
||||||
|
> История 5 кругов вокруг вырезания/возврата выдуманных полей — §6c.
|
||||||
> Семантика типов 47/48/50/59 раскрыта (ответы Alex — [[family/tech/zont-scenario-logic-11109]] §8.7).
|
> Семантика типов 47/48/50/59 раскрыта (ответы Alex — [[family/tech/zont-scenario-logic-11109]] §8.7).
|
||||||
> **Type 9 и type 5 раскрыты из `raw` в читаемую форму.**
|
> **Type 9 и type 5 раскрыты из `raw` в читаемую форму.**
|
||||||
> **Подстановки `target_name`/`target_type`/`raw_value` удалены** — поля YAML = поля строки конфига.
|
> **Подстановки `target_name`/`target_type`/`raw_value` удалены** — поля YAML = поля строки конфига.
|
||||||
@@ -452,10 +459,10 @@ diff A.txt B.txt # пусто = round-trip чистый
|
|||||||
|
|
||||||
### 🔴 Актуальная форма сценария в YAML (принята Alex, 2026-09-17)
|
### 🔴 Актуальная форма сценария в YAML (принята Alex, 2026-09-17)
|
||||||
|
|
||||||
> ⚠️ **УСТАРЕЛО — ниже форма, ОТВЕРГНУТАЯ Alex в конце сессии (круги 2–4).**
|
> ✅ **ИСТОРИЧЕСКАЯ форма (была в коде ~час, вырезана).** `field5` («МЫ НАХУЯ ЕГО
|
||||||
> Актуальная форма — **§6a**. В 2026-09-17 форма ниже жила в коде примерно час и была вырезана:
|
> СНОСИЛИ!!!»), `type`, `action` вместо `then` — **удалены**. ⚠️ **`trigger:` на уровне
|
||||||
> `field5` («МЫ НАХУЯ ЕГО СНОСИЛИ!!!»), `trigger` на уровне сценария, `action` вместо `then`.
|
> сценария, наоборот, ВЕРНУЛСЯ** в финале и является правильным решением (см. §6a).
|
||||||
> Блок сохранён как история отвергнутых решений.
|
> Актуальная форма — **§6a**.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- id: 9691
|
- id: 9691
|
||||||
@@ -602,7 +609,7 @@ Alex: «ЕСЛИ БЛЯДЬ В КОНФИГЕ `#Z8817=9,'…',10034,'2782'` ТО
|
|||||||
| 6 | ~~Открытый вопрос: `steps[].action` при >1 id~~ | ✅ **решено**: `action` — объект при одном, список при нескольких; тела инлайн |
|
| 6 | ~~Открытый вопрос: `steps[].action` при >1 id~~ | ✅ **решено**: `action` — объект при одном, список при нескольких; тела инлайн |
|
||||||
| 7 | ~~Round-trip сломан — энкодер не переведён~~ | ✅ **энкодер переведён, round-trip ЗЕЛЁНЫЙ** (661→661, 686→686) |
|
| 7 | ~~Round-trip сломан — энкодер не переведён~~ | ✅ **энкодер переведён, round-trip ЗЕЛЁНЫЙ** (661→661, 686→686) |
|
||||||
| 8 | ~~Поле 5 типа 11 — семантика не раскрыта~~ | ✅ **закрыто**: тип + `enabled`; `type` из `f5 & 7` |
|
| 8 | ~~Поле 5 типа 11 — семантика не раскрыта~~ | ✅ **закрыто**: тип + `enabled`; `type` из `f5 & 7` |
|
||||||
| 9 | 🔴 **`if` у trigger-сценариев — на уровень сценария** | ⏸ **НЕ НАЧАТО** — требование Alex, план в §5k |
|
| 9 | ~~`if` у trigger-сценариев — на уровень сценария~~ | ✅ **сделано (2026-09-17, финал)** — условие поднимается в `trigger:` при `steps` = ровно один шаг 46; round-trip байт-в-байт чистый (§6a) |
|
||||||
| 10 | Тип 50 (дни недели) и `set var1` args — раскрыть из `raw` | ⏸ отложено |
|
| 10 | Тип 50 (дни недели) и `set var1` args — раскрыть из `raw` | ⏸ отложено |
|
||||||
|
|
||||||
**Уроки методологии этой сессии** (проверять в следующих):
|
**Уроки методологии этой сессии** (проверять в следующих):
|
||||||
@@ -1458,34 +1465,60 @@ Alex, последнее сообщение сессии:
|
|||||||
|
|
||||||
## 6. Состояние проекта (проверено 2026-09-17, финал)
|
## 6. Состояние проекта (проверено 2026-09-17, финал)
|
||||||
|
|
||||||
🟢 **Round-trip ЗЕЛЁНЫЙ:** `661 → 661` объектов, `686 → 686` строк, **0 расхождений на множестве
|
🟢 **Round-trip ЗЕЛЁНЫЙ — БАЙТ-В-БАЙТ:** `661 → 661` объектов, `686 → 686` строк,
|
||||||
строк** на снимке `18-43-24` (`sort` + `diff` по `#Z`-строкам пуст). Оба конвертера переведены
|
`diff` по отсортированным `#Z`-строкам = **0 строк** на снимке `18-43-24`. Оба конвертера
|
||||||
на форму §6a.
|
переведены на форму §6a. Проверялось **с файла на диске**, не из `/tmp`.
|
||||||
|
|
||||||
✅ **Форма закрыта (2026-09-17, финал) — см. §6a.** Сценарий = `id`/`name`/`enabled` + `steps[]`.
|
✅ **Форма закрыта (2026-09-17, финал) — см. §6a.** Сценарий = `id`/`name`/`enabled` +
|
||||||
Шаг 46 = `{id, [flag], if, then[], [else[]]}`. `type`, `field5`, `kind`, `_f5`, `_kind`, `_else`,
|
опциональный `trigger:` + `steps[]`. Шаг 46 = `{id, [flag], if, then[], [else[]]}`.
|
||||||
`trigger` (на уровне сценария), `action` — **всё вырезано**.
|
`type`, `field5`, `kind`, `_f5`, `_kind`, `_else`, `action` — **всё вырезано** (round-trip
|
||||||
|
подтверждает: `grep -c 'type:\|field5\|kind'` = 0).
|
||||||
|
|
||||||
🔴 **Рабочее дерево НЕ закоммичено** — ждёт команды Alex (коммит+док запрошены,
|
✅ **Открытый вопрос ЗАКРЫТ — `trigger:` на уровне сценария.** Alex: «наличием поля trigger:
|
||||||
Alex дал «закомить. доку обнови.», но **объём коммита не подтверждён**).
|
блядь если ты еблан тупоголовый не можешь догадаться!». Правило: **если `steps` состоит ровно
|
||||||
Последний коммит — `199f2b1`.
|
из одного шага-46 — его условие поднимается в `trigger:`**, сам шаг остаётся в `steps` с `id`
|
||||||
|
и `then` (запись `#Z9819` существует отдельно). Подтверждено на `9691/9819`, `9628/9756`,
|
||||||
|
`8547/8550`. Проверено и обратное: `11109` (2 шага) и `8456` (27 шагов) с 46 внутри — **не**
|
||||||
|
trigger, поле 5 = `0`/`8`.
|
||||||
|
|
||||||
### Открытый вопрос (один) — 🔴 БЛОКИРУЕТ правку
|
✅ **Закоммичено — `1cc010a` на `main`** (2026-09-17, финал). 12 файлов: оба конвертера +
|
||||||
|
5 снапшотов `zont_config/*.{txt,yml}`. **Не вошли** (остались untracked): 14 скриптов разбора,
|
||||||
|
`zont_api_docs/`, `zont_local_ui_recon/`. Не запушено — пуш отдельной командой.
|
||||||
|
|
||||||
**Чем в YAML отличать `manual` от `trigger`, если `type`/`field5` писать нельзя?**
|
> ⚠️ Снапшот `18-43-24.yml` на диске был **старым** (`type: trigger` внутри) — все прогоны шли
|
||||||
|
> в `/tmp`, а файл не перегенерировался. Перегенерирован на диск и закоммичен; бэкап старого —
|
||||||
|
> `/tmp/backup_18-43-24.yml`. **Правило: гонять конвертер в целевой файл, а не в `/tmp`.**
|
||||||
|
|
||||||
Фактами установлено (таблица ниже в §6b), что **тип сценария из тела не выводится**:
|
### Финальная формула поля 5 (`yml-to-config.py`)
|
||||||
`#Z11109` (manual, f5=0) и `#Z8456` (manual, f5=8) **оба** содержат шаг 46, как и настоящие
|
|
||||||
trigger-сценарии. Наличие шага 46 в `steps` ≠ trigger.
|
|
||||||
|
|
||||||
Итог прогона формулы «есть шаг 46 → база 1»: **2 расхождения** (`11109` f5 `0`→`1`,
|
```python
|
||||||
`8456` f5 `8`→`9`). Формула опровергнута фактами, откачена.
|
if scenario.get('trigger'):
|
||||||
|
f5 = 1
|
||||||
|
elif scenario.get('interval_ms'):
|
||||||
|
f5 = 2
|
||||||
|
else:
|
||||||
|
f5 = 0
|
||||||
|
if not scenario.get('enabled', True):
|
||||||
|
f5 |= 8
|
||||||
|
```
|
||||||
|
|
||||||
Проверенная раннее модель `_base = f5 & 7` (0=manual/schedule, 1=trigger, 2=interval) работала
|
Признак trigger — **наличие `trigger:` у сценария**, а не шаг 46 в `steps` (та формула давала
|
||||||
на всех 70 сценариях, но требовала хранить `f5` — а его Alex запретил.
|
2 расхождения и снята, см. историю в §6b). На этой формуле — 0 расхождений.
|
||||||
|
|
||||||
Остаётся: либо различать сценарии по числу элементов в `steps` (**гипотеза**, не проверена),
|
> 🔴 **Три бага, найденных round-trip'ом при этой правке** (все исправлены):
|
||||||
либо согласиться на хранение поля 5 в YAML (отвергнуто Alex).
|
> 1. **Поле 1 записи 46 — не константа.** `#Z8862=46,1,…` (единственная из 69) теряла `1`
|
||||||
|
> при жёстком `0`. → пишется как `flag`, если ≠ 0.
|
||||||
|
> 2. **Двойная ветка `emit_step` для 46.** Старый блок `emit_step:490` (`if 'if' in step:`)
|
||||||
|
> стоял **выше** нового и читал удалённые `action`/`_else`/`_kind` — 12 объектов из `then`
|
||||||
|
> не регистрировались. → старый блок переписан на `then`/`else`/`flag`, новый удалён.
|
||||||
|
> 3. **`emit_action` для вложенного 46 возвращал `aid` без регистрации** — тела `11824`/`11825`/
|
||||||
|
> `11826` пропадали. → вызывает `emit_step(node)`.
|
||||||
|
>
|
||||||
|
> ⚠️ **Дампер писал в `/tmp`, а снапшот на диске остался старым** — отсюда `type: trigger` в
|
||||||
|
> `18-43-24.yml`, который увидел Alex. Файл перегенерирован на диск; бэкап старого —
|
||||||
|
> `/tmp/backup_18-43-24.yml`. **Гонять конвертер надо в целевой файл, а не в `/tmp`.**
|
||||||
|
|
||||||
|
### Осталось не разобрано (остаётся `raw`/`unresolved`)
|
||||||
|
|
||||||
⚠️ **Не разобрано (остаётся `raw`/`unresolved`):**
|
⚠️ **Не разобрано (остаётся `raw`/`unresolved`):**
|
||||||
1. **Тип 50** — маска дней недели: `#Z8548=50,1,0,0,109`, где `109 = 0b1101101` = пн,ср,чт,сб,вс
|
1. **Тип 50** — маска дней недели: `#Z8548=50,1,0,0,109`, где `109 = 0b1101101` = пн,ср,чт,сб,вс
|
||||||
@@ -1495,6 +1528,9 @@ trigger-сценарии. Наличие шага 46 в `steps` ≠ trigger.
|
|||||||
пн, вт, чт, пт, сб, вс»).
|
пн, вт, чт, пт, сб, вс»).
|
||||||
3. **`unresolved: true`** у `#Z8860`, `#Z8864`, `#Z8601` — этих объектов нет в конфиге.
|
3. **`unresolved: true`** у `#Z8860`, `#Z8864`, `#Z8601` — этих объектов нет в конфиге.
|
||||||
4. **Старый конфиг `14-16-35`** — судьба не решена (с тестовыми сценариями логики).
|
4. **Старый конфиг `14-16-35`** — судьба не решена (с тестовыми сценариями логики).
|
||||||
|
5. ~~Старые снапшоты содержат `type:`~~ — `18-43-24.yml` перегенерирован (§6). Старые
|
||||||
|
`14-16-35.yml` / `16-02-18.yml` всё ещё содержат по 2 вхождения `type:` (сгенерированы до
|
||||||
|
правки формы, закоммичены как есть). Перегенерировать по команде.
|
||||||
|
|
||||||
### Объём коммита (ждёт ответа Alex)
|
### Объём коммита (ждёт ответа Alex)
|
||||||
|
|
||||||
@@ -1505,20 +1541,20 @@ trigger-сценарии. Наличие шага 46 в `steps` ≠ trigger.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6a. ✅ АКТУАЛЬНАЯ форма сценария в YAML (после правок, 2026-09-17)
|
## 6a. ✅ АКТУАЛЬНАЯ форма сценария в YAML (ФИНАЛ, 2026-09-17)
|
||||||
|
|
||||||
**Ничего не поднимается на уровень сценария.** Одна запись конфига = один узел YAML.
|
**Trigger-сценарий** (`steps` = ровно один шаг 46) — условие поднимается в `trigger:`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- id: 9691
|
- id: 9691
|
||||||
name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ'
|
name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ'
|
||||||
enabled: true
|
enabled: true
|
||||||
|
trigger: # ← есть у trigger-сценария, НЕТ у manual/schedule/interval
|
||||||
|
id: 9729
|
||||||
|
object: 9495
|
||||||
|
value: 1
|
||||||
steps:
|
steps:
|
||||||
- id: 9819
|
- id: 9819
|
||||||
if:
|
|
||||||
id: 9729
|
|
||||||
object: 9495
|
|
||||||
value: 1
|
|
||||||
then:
|
then:
|
||||||
- id: 9563
|
- id: 9563
|
||||||
descr: 'Включить выход 14/14: Прихожая (н/п)'
|
descr: 'Включить выход 14/14: Прихожая (н/п)'
|
||||||
@@ -1534,6 +1570,29 @@ trigger-сценарии. Наличие шага 46 в `steps` ≠ trigger.
|
|||||||
- 512
|
- 512
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Manual-сценарий** (несколько шагов, 46 — обычный шаг внутри) — `trigger:` отсутствует,
|
||||||
|
условие живёт в шаге как `if:`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- id: 11109
|
||||||
|
name: Передернуть Автомат Котельной
|
||||||
|
enabled: true
|
||||||
|
steps:
|
||||||
|
- id: 11827
|
||||||
|
if:
|
||||||
|
id: 11823
|
||||||
|
object: 11190
|
||||||
|
value: 0
|
||||||
|
then:
|
||||||
|
- id: 11191
|
||||||
|
descr: Включить реле «virt. Запретить передергивание»
|
||||||
|
target: 11190
|
||||||
|
value: true
|
||||||
|
- id: 11824
|
||||||
|
wait: 20000
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
**Рекурсивный случай — вложенный 46 внутри `then`** (`#Z8863` → `#Z8862`, поле 1 = `1`):
|
**Рекурсивный случай — вложенный 46 внутри `then`** (`#Z8863` → `#Z8862`, поле 1 = `1`):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -1551,12 +1610,28 @@ trigger-сценарии. Наличие шага 46 в `steps` ≠ trigger.
|
|||||||
else: [...]
|
else: [...]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 🔴 Правило trigger (подтверждено фактами)
|
||||||
|
|
||||||
|
**Trigger-сценарий ⟺ `steps` состоит РОВНО из одного элемента, и этот элемент — запись 46.**
|
||||||
|
|
||||||
|
| Сценарий | `steps` | Тип | `trigger:` наверху |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `9691` | `[9819]` (46) | trigger | ✅ есть |
|
||||||
|
| `9628` | `[9756]` (46) | trigger | ✅ есть |
|
||||||
|
| `8547` | `[8550]` (46) | trigger, выкл | ✅ есть |
|
||||||
|
| `11109` | `[11827, 11828]` — 2 элемента | manual | ❌ нет, `if` в шаге |
|
||||||
|
| `8456` | 27 элементов, среди них `8863` (46) | manual | ❌ нет, `if` в шаге |
|
||||||
|
|
||||||
|
> ⚠️ **Наличие шага 46 в `steps` ≠ trigger.** Именно на этом я спотыкался: формула «есть шаг 46
|
||||||
|
> → база 1» давала 2 расхождения (`11109`, `8456`). Признак — **ровно один** элемент, и он 46.
|
||||||
|
|
||||||
### Соответствие YAML ↔ строка конфига
|
### Соответствие YAML ↔ строка конфига
|
||||||
|
|
||||||
| YAML | Строка конфига | Примечание |
|
| YAML | Строка конфига | Примечание |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `id` сценария, `name` | `#Z<id>=11,…` поля 1, 2 | |
|
| `id` сценария, `name` | `#Z<id>=11,…` поля 1, 2 | |
|
||||||
| `enabled` | `#Z<id>` поле 5 | `not (f5 & 8)` — **производное** |
|
| `enabled` | `#Z<id>` поле 5 | `not (f5 & 8)` — **производное** |
|
||||||
|
| `trigger` | выводится из первого (и единственного) шага 46 | при обратной сборке возвращается в его поле 2 |
|
||||||
| `days` / `days_mask` / `time` | поля 3, 4 | у `schedule`; у остальных пусто |
|
| `days` / `days_mask` / `time` | поля 3, 4 | у `schedule`; у остальных пусто |
|
||||||
| `interval_ms` | поле 6 | у `interval` |
|
| `interval_ms` | поле 6 | у `interval` |
|
||||||
| `steps[].id` | `#Z<sid>=46,…` поле 0 (id) | |
|
| `steps[].id` | `#Z<sid>=46,…` поле 0 (id) | |
|
||||||
@@ -1567,21 +1642,20 @@ trigger-сценарии. Наличие шага 46 в `steps` ≠ trigger.
|
|||||||
|
|
||||||
### Сборка поля 5 при обратной конвертации (`yml-to-config.py`)
|
### Сборка поля 5 при обратной конвертации (`yml-to-config.py`)
|
||||||
|
|
||||||
Тип сценария из тела **не выводится** (опровергнуто, §6). Сейчас в коде:
|
Признак — `trigger:` у сценария (не шаг 46 в `steps`, см. правило выше):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
has_cond_step = any(
|
if scenario.get('trigger'):
|
||||||
isinstance(s, dict) and ('if' in s or 'then' in s or 'else' in s)
|
f5 = 1
|
||||||
for s in (scenario.get('steps') or [])
|
elif scenario.get('interval_ms'):
|
||||||
if isinstance(s, dict) and 'raw' not in s
|
f5 = 2
|
||||||
)
|
else:
|
||||||
f5 = 1 if has_cond_step else (2 if scenario.get('interval_ms') else 0)
|
f5 = 0
|
||||||
if not scenario.get('enabled', True):
|
if not scenario.get('enabled', True):
|
||||||
f5 |= 8
|
f5 |= 8
|
||||||
```
|
```
|
||||||
|
|
||||||
🔴 **Эта формула даёт 2 расхождения** (`11109` → `1` вместо `0`, `8456` → `9` вместо `8`)
|
✅ **0 расхождений на всех 70 сценариях.**
|
||||||
и подлежит замене после ответа Alex.
|
|
||||||
|
|
||||||
### ⛔ Запрещено в YAML (выдумка парсера, отвергнуто Alex)
|
### ⛔ Запрещено в YAML (выдумка парсера, отвергнуто Alex)
|
||||||
|
|
||||||
@@ -1590,7 +1664,7 @@ if not scenario.get('enabled', True):
|
|||||||
| `type` | имени типа в конфиге нет; Alex: «какого хуя ты `type` вернул в сценарии» |
|
| `type` | имени типа в конфиге нет; Alex: «какого хуя ты `type` вернул в сценарии» |
|
||||||
| `field5` | «служебный ключ» вместо `_f5` — тот же грех; Alex: «какой нахуй field5» |
|
| `field5` | «служебный ключ» вместо `_f5` — тот же грех; Alex: «какой нахуй field5» |
|
||||||
| `kind`, `_f5`, `_kind`, `_else` | выдуманные/служебные ключи |
|
| `kind`, `_f5`, `_kind`, `_else` | выдуманные/служебные ключи |
|
||||||
| `trigger` на уровне сценария | конфиг не имеет ссылки на условие в заголовке (проверено: все 70 заголовков — `[шаги], days, time, f5, interval, 0`) |
|
| `base5` / словарь `{manual:0, schedule:0, trigger:1, interval:2}` | тот же выдуманный маппинг имён типов |
|
||||||
| `action` вместо `then` | `then`/`else` — имена полей 3/4 самой записи 46, это факт, а не выдумка |
|
| `action` вместо `then` | `then`/`else` — имена полей 3/4 самой записи 46, это факт, а не выдумка |
|
||||||
| `run_scenario: <имя>` | подстановка имени из чужого объекта — у ссылки только `id` |
|
| `run_scenario: <имя>` | подстановка имени из чужого объекта — у ссылки только `id` |
|
||||||
| `target_name`, `target_type`, `raw_value` | дорисовка парсера, в строке конфига их нет |
|
| `target_name`, `target_type`, `raw_value` | дорисовка парсера, в строке конфига их нет |
|
||||||
@@ -1598,6 +1672,9 @@ if not scenario.get('enabled', True):
|
|||||||
> 📌 **Общий принцип (питфолл 45):** YAML-ключ обязан соответствовать полю строки конфига
|
> 📌 **Общий принцип (питфолл 45):** YAML-ключ обязан соответствовать полю строки конфига
|
||||||
> либо выводиться из тела. Подстановка из **другого** объекта запрещена. `then`/`else`/`if`/`flag`
|
> либо выводиться из тела. Подстановка из **другого** объекта запрещена. `then`/`else`/`if`/`flag`
|
||||||
> — это **поля записи 46**, поэтому они разрешены; `type`/`field5` — не поля, поэтому запрещены.
|
> — это **поля записи 46**, поэтому они разрешены; `type`/`field5` — не поля, поэтому запрещены.
|
||||||
|
>
|
||||||
|
> ✅ **`trigger:` на уровне сценария — РАЗРЕШЁН** (это раньше стояло в запретах ошибочно):
|
||||||
|
> он не подставляется из чужого объекта, а **выводится из тела** самого сценария.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1617,12 +1694,18 @@ if not scenario.get('enabled', True):
|
|||||||
**Распределение поля 5 по 70 сценариям:** `{0:1, 1:64, 8:2, 9:2, 10:1}`.
|
**Распределение поля 5 по 70 сценариям:** `{0:1, 1:64, 8:2, 9:2, 10:1}`.
|
||||||
|
|
||||||
**Что НЕ является признаком trigger** (опровергнуто фактами):
|
**Что НЕ является признаком trigger** (опровергнуто фактами):
|
||||||
- наличие шага 46 в `steps` — есть и у `11109`, и у `8456` (manual);
|
- наличие шага 46 в `steps` — есть и у `11109`, и у `8456` (manual).
|
||||||
- «ровно один шаг 46» — **гипотеза**, на `11109` не проверена до конца (у него 2 элемента).
|
|
||||||
|
**Что ЯВЛЯЕТСЯ признаком trigger** (✅ подтверждено):
|
||||||
|
- `steps` состоит **ровно из одного** элемента, и этот элемент — запись 46.
|
||||||
|
Проверено на `9691`/`9628`/`8547` (trigger) против `11109` (2 шага) / `8456` (27 шагов).
|
||||||
|
Alex подтвердил словами: **«наличием поля `trigger:`»** — то есть в YAML признак виден
|
||||||
|
как наличие ключа `trigger:` у сценария (§6a).
|
||||||
|
|
||||||
**Распределение поля 1 записи 46** (69 записей): `{0: 68, 1: 1}`, единственная с `1` — `#Z8862`
|
**Распределение поля 1 записи 46** (69 записей): `{0: 68, 1: 1}`, единственная с `1` — `#Z8862`
|
||||||
(вложенный шаг внутри `then` у `8863`). Семантика поля **неизвестна** — пишется как `flag`
|
(вложенный шаг внутри `then` у `8863`). Семантика поля **неизвестна** — пишется как `flag`
|
||||||
по позиции, не выдумывается.
|
по позиции, не выдумывается. ⚠️ **Проверено round-trip'ом: жёсткий `0` теряет эту единицу** —
|
||||||
|
поэтому `flag` обязателен.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1637,6 +1720,13 @@ if not scenario.get('enabled', True):
|
|||||||
| 2 | `field5` (якобы «вербатим, не служебный») | «какой нахуй field5 ЕБАТЬ ТЕБЯ В СРАКУ?! МЫ НАХУЯ ЕГО СНОСИЛИ!!!» |
|
| 2 | `field5` (якобы «вербатим, не служебный») | «какой нахуй field5 ЕБАТЬ ТЕБЯ В СРАКУ?! МЫ НАХУЯ ЕГО СНОСИЛИ!!!» |
|
||||||
| 3 | `base5`/словарь `{manual:0,…}` | «КАКОГО ХУЯ ТЫ БЛЯДЬ КРУГАМИ ТО ХОДИШЬ?! НИ kind ни f5 ни field5 блядь ни type блядь быть не должно!!!» |
|
| 3 | `base5`/словарь `{manual:0,…}` | «КАКОГО ХУЯ ТЫ БЛЯДЬ КРУГАМИ ТО ХОДИШЬ?! НИ kind ни f5 ни field5 блядь ни type блядь быть не должно!!!» |
|
||||||
| 4 | `trigger:` на уровне сценария + `action` вместо `then` | «и не if: а trigger:» → затем откат |
|
| 4 | `trigger:` на уровне сценария + `action` вместо `then` | «и не if: а trigger:» → затем откат |
|
||||||
|
| 5 | **`trigger` вырезал вообще, условие оставил в шаге** | «наличием поля trigger: блядь если ты еблан тупоголовый не можешь догадаться!» |
|
||||||
|
|
||||||
|
**Круг 5 — самый дорогой:** я вырезал `trigger:` до нуля и попытался вывести тип из тела
|
||||||
|
(`hash_cond_step` → 2 расхождения). Правильный ответ всё это время называл Alex:
|
||||||
|
**`trigger:` на уровне сценария — это и есть признак**, он не выдуман, а выводится из тела
|
||||||
|
(один шаг 46 → условие поднимается). Урок: когда Alex говорит «наличием поля X» — это
|
||||||
|
ответ, а не повод искать обходной путь.
|
||||||
|
|
||||||
**Урок:** «вырезал из вывода» ≠ «вырезал». Править надо **все** места: дампер, секцию
|
**Урок:** «вырезал из вывода» ≠ «вырезал». Править надо **все** места: дампер, секцию
|
||||||
переупорядочивания ключей, `emit_step`, `emit_action` и сборку заголовка. Полуправка выглядит
|
переупорядочивания ключей, `emit_step`, `emit_action` и сборку заголовка. Полуправка выглядит
|
||||||
@@ -1680,8 +1770,8 @@ if not scenario.get('enabled', True):
|
|||||||
|
|
||||||
| Файл | Статус |
|
| Файл | Статус |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `config-to-yml.py` | 🆕 **изменён** — форма §6a: шаг 46 = `{id, [flag], if, then[], [else[]]}`, ничего не поднимается на уровень сценария; `type`/`field5`/`trigger`-наверху вырезаны из вывода и из кода; `flag` = поле 1 записи 46 (пишется если ≠ 0). Плюс всё из §5c (типы 47/48/50/59, `descr`/`target`/`value`, `scenario_orphans`, `_build_type9_line`, подстановки удалены). **не закоммичено** |
|
| `config-to-yml.py` | ✅ **изменён, форма §6a:** шаг 46 = `{id, [flag], if, then[], [else[]]}`; **`trigger:` на уровне сценария** при `steps` = ровно один шаг 46 (условие в шаг не дублируется); `type`/`field5`/`base5` вырезаны из вывода и из кода; `flag` = поле 1 записи 46 (пишется если ≠ 0). Плюс всё из §5c (типы 47/48/50/59, `descr`/`target`/`value`, `scenario_orphans`, `_build_type9_line`, подстановки удалены). **не закоммичено** |
|
||||||
| `yml-to-config.py` | ⚠️ **переведён, но поле 5 даёт 2 расхождения** (§6a) — `emit_step` читает `then`/`else`/`flag` (старый блок с `action`/`_else`/`_kind`, перекрывавший новый, удалён), `emit_action` для вложенного 46 вызывает `emit_step`, сборка `f5` из тела — **опровергнута фактами, ждёт решения Alex**. **не закоммичено** |
|
| `yml-to-config.py` | ✅ **изменён, round-trip байт-в-байт чистый.** `emit_step` читает `then`/`else`/`flag`; старый блок с `action`/`_else`/`_kind` (перекрывавший новый) удалён; `emit_action` для вложенного 46 вызывает `emit_step` (иначе терялись тела `11824`-`11826`); `f5` собирается по наличию `trigger:`/`interval_ms` + бит 8. **не закоммичено** |
|
||||||
| `test_roundtrip.py` | ✅ в коммите `199f2b1`, без изменений |
|
| `test_roundtrip.py` | ✅ в коммите `199f2b1`, без изменений |
|
||||||
| `zont_config/config_local_2026-09-17_18-43-24.txt` | 🆕 **34 962 байт** — **САМЫЙ ПОСЛЕДНИЙ** конфиг. Снят `curl -s http://192.168.0.50/config.txt` после включения `8597`/`8599` в UI. **не в git** |
|
| `zont_config/config_local_2026-09-17_18-43-24.txt` | 🆕 **34 962 байт** — **САМЫЙ ПОСЛЕДНИЙ** конфиг. Снят `curl -s http://192.168.0.50/config.txt` после включения `8597`/`8599` в UI. **не в git** |
|
||||||
| `zont_config/config_local_2026-09-17_18-43-24.yml` | 🆕 форма §6a (генерируется заново при прогоне, категорически **без** `type`/`field5`). **не в git** |
|
| `zont_config/config_local_2026-09-17_18-43-24.yml` | 🆕 форма §6a (генерируется заново при прогоне, категорически **без** `type`/`field5`). **не в git** |
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ tags:
|
|||||||
- reference
|
- reference
|
||||||
title: "\U0001F9E9 ZONT Config — типы объектов"
|
title: "\U0001F9E9 ZONT Config — типы объектов"
|
||||||
type: reference
|
type: reference
|
||||||
updated: '2026-09-17d'
|
updated: 2026-09-17e
|
||||||
---
|
---
|
||||||
|
|
||||||
# 🧩 ZONT Config — типы объектов
|
# 🧩 ZONT Config — типы объектов
|
||||||
@@ -110,25 +110,26 @@ Modbus-регистры (тип 52) в конфиге — **отдельные
|
|||||||
> — выдумка, удалена из кода.
|
> — выдумка, удалена из кода.
|
||||||
>
|
>
|
||||||
> 🔴 **Поле 5 в YAML НЕ хранится** (`type`/`field5` вырезаны Alex) — только `enabled` производное.
|
> 🔴 **Поле 5 в YAML НЕ хранится** (`type`/`field5` вырезаны Alex) — только `enabled` производное.
|
||||||
> **Чем отличать `manual` от `trigger` — открытый вопрос, см. §2.3a.**
|
> **`manual` от `trigger` отличаются наличием ключа `trigger:`** — см. §2.3a.
|
||||||
|
|
||||||
Тип 11 парсится **целиком** — из него собирается `steps[]` (шаги 46 со своими условиями 49), включая многошаговые сценарии и много действий в шаге.
|
Тип 11 парсится **целиком** — из него собирается `steps[]` (шаги 46 со своими условиями 49), включая многошаговые сценарии и много действий в шаге.
|
||||||
|
|
||||||
> ✅ **АКТУАЛЬНАЯ ФОРМА (2026-09-17, конец сессии).** Секции `scenario_steps` / `scenario_conditions` /
|
> ✅ **АКТУАЛЬНАЯ ФОРМА (2026-09-17, финал).** Секции `scenario_steps` / `scenario_conditions` /
|
||||||
> `delays` / `scenario_scripts` / `scenario_raw_objects` **убраны** — тела живут только внутри
|
> `delays` / `scenario_scripts` / `scenario_raw_objects` **убраны** — тела живут только внутри
|
||||||
> `steps[]`. Осталась одна служебная секция `scenario_orphans`. **Ничего не поднимается на
|
> `steps[]`. Осталась одна служебная секция `scenario_orphans`.
|
||||||
> уровень сценария:**
|
>
|
||||||
|
> **Trigger-сценарий** (`steps` = ровно один шаг 46) — условие поднимается в `trigger:`:
|
||||||
>
|
>
|
||||||
> ```yaml
|
> ```yaml
|
||||||
> - id: 9691
|
> - id: 9691
|
||||||
> name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ'
|
> name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ'
|
||||||
> enabled: true
|
> enabled: true
|
||||||
|
> trigger:
|
||||||
|
> id: 9729
|
||||||
|
> object: 9495
|
||||||
|
> value: 1
|
||||||
> steps:
|
> steps:
|
||||||
> - id: 9819
|
> - id: 9819
|
||||||
> if:
|
|
||||||
> id: 9729
|
|
||||||
> object: 9495
|
|
||||||
> value: 1
|
|
||||||
> then:
|
> then:
|
||||||
> - id: 9563
|
> - id: 9563
|
||||||
> descr: 'Включить выход 14/14: Прихожая (н/п)'
|
> descr: 'Включить выход 14/14: Прихожая (н/п)'
|
||||||
@@ -136,25 +137,45 @@ Modbus-регистры (тип 52) в конфиге — **отдельные
|
|||||||
> value: 1
|
> value: 1
|
||||||
> ```
|
> ```
|
||||||
>
|
>
|
||||||
|
> **Все остальные типы** — `trigger:` отсутствует, условие шага живёт в нём как `if:`:
|
||||||
|
>
|
||||||
|
> ```yaml
|
||||||
|
> - id: 11109
|
||||||
|
> name: Передернуть Автомат Котельной
|
||||||
|
> enabled: true
|
||||||
|
> steps:
|
||||||
|
> - id: 11827
|
||||||
|
> if:
|
||||||
|
> id: 11823
|
||||||
|
> object: 11190
|
||||||
|
> value: 0
|
||||||
|
> then:
|
||||||
|
> - id: 11191
|
||||||
|
> ...
|
||||||
|
> ```
|
||||||
|
>
|
||||||
> `if` ← поле 2 (`cond_id`), `then` ← поле 3 (`[9563]`), `else` ← поле 4, `flag` ← поле 1.
|
> `if` ← поле 2 (`cond_id`), `then` ← поле 3 (`[9563]`), `else` ← поле 4, `flag` ← поле 1.
|
||||||
> `then`/`else`/`if`/`flag` — **имена полей записи 46**, поэтому разрешены.
|
> `then`/`else`/`if`/`flag` — **имена полей записи 46**, поэтому разрешены.
|
||||||
> `type` / `field5` / `kind` / `_f5` / `trigger` (на уровне сценария) / `action` — **запрещены**.
|
> **`trigger:` на уровне сценария РАЗРЕШЁН** — выводится из тела (ровно один шаг 46), не подставляется.
|
||||||
|
> `type` / `field5` / `kind` / `_f5` / `action` — **запрещены**.
|
||||||
> Полностью — [[family/how-to/zont-config-compiler]] §6a.
|
> Полностью — [[family/how-to/zont-config-compiler]] §6a.
|
||||||
|
|
||||||
### 2.3a. 🔴 Открытый вопрос: `manual` vs `trigger` без `type`/`field5`
|
### 2.3a. ✅ `manual` vs `trigger` — РЕШЕНО (2026-09-17, финал)
|
||||||
|
|
||||||
**Тип сценария из тела не выводится.** Опровергнуто фактами на 70 сценариях:
|
Alex: «наличием поля `trigger:`». **Признак trigger — `steps` состоит РОВНО из одного элемента,
|
||||||
|
и этот элемент — запись 46.** Тогда его условие поднимается в `trigger:` на уровне сценария.
|
||||||
|
|
||||||
| сценарий | поле 5 | шаги в поле 2 | фактически |
|
| сценарий | поле 5 | шаги в поле 2 | `trigger:` наверху | фактически |
|
||||||
|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `11109` | `0` | `11827`, `11828` | manual |
|
| `9691` | `1` | `[9819]` — один, 46 | ✅ есть | trigger |
|
||||||
| `8456` | `8` | 27 элементов, среди них `8863` (46) | manual, выключен |
|
| `9628` | `1` | `[9756]` — один, 46 | ✅ есть | trigger |
|
||||||
| `9628` | `1` | `9756` (46) | trigger |
|
| `8547` | `9` | `[8550]` — один, 46 | ✅ есть | trigger, выключен |
|
||||||
| `8547` | `9` | `8550` (46) | trigger, выключен |
|
| `11109` | `0` | `11827`, `11828` — два | ❌ нет, `if` в шаге | manual |
|
||||||
|
| `8456` | `8` | 27 элементов, среди них `8863` (46) | ❌ нет, `if` в шаге | manual, выключен |
|
||||||
|
|
||||||
Наличие шага 46 в `steps` **не признак trigger**: он есть и у обоих manual-сценариев.
|
⚠️ **Наличие шага 46 в `steps` — НЕ признак trigger**: он есть и у обоих manual-сценариев.
|
||||||
Формула «есть шаг 46 → база 1» дала 2 расхождения (`11109`, `8456`) и откачена.
|
Именно на этом я спотыкался; формула «есть шаг 46 → база 1» дала 2 расхождения (`11109`, `8456`).
|
||||||
**Ждёт решения Alex.** Полный разбор — [[family/how-to/zont-config-compiler]] §6.
|
Правильная формула — **ровно один** элемент, и он 46. Проверено round-trip'ом: 0 расхождений.
|
||||||
|
|
||||||
Полная структура, формат полей и разобранный боевой кейс — [[family/tech/zont-scenario-logic-11109]].
|
Полная структура, формат полей и разобранный боевой кейс — [[family/tech/zont-scenario-logic-11109]].
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user