Files
obsidian-vault/family/tech/zont-scenario-logic-11109.md
T

259 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 8.17. ✅ МОДЕЛЬ ЗАКРЫТА (2026-09-17, финал) — поле 5, форма сценария, type 47/49
> 🔴 **ЧАСТИЧНО УСТАРЕЛО — актуальная форма в [[family/how-to/zont-config-compiler]] §8.**
> Что здесь неверно:
> - **`if`/`then`/`else` НЕ выдумка** (§(б) ниже утверждает обратное). Это **поля записи 46**
> — поле 3 = `then`-список, поле 4 = `else`-список, поле 2 = условие. Alex: «Then конечно!!!».
> Пара `then`/`else` обязательна там, где у шага есть свой `if`.
> - **`action`** — не выдумка, но и не универсальное имя: у шага **с поднятым условием**
> (`trigger:` наверху) список действий называется `action`; у шага **со своим `if`** — `then`.
> - **`trigger:` не дублируется** в шаге: условие **переносится** (`pop`), иначе PyYAML ставит
> анкоры `&id061`/`*id061` (было 69 штук).
> - **`_kind`/`_f5`/`_then`** в примерах §(б) — выдуманные служебные ключи, вырезаны.
> - **Поле 5 в YAML не хранится вообще** (`type`/`field5` тоже вырезаны) — собирается из тела.
> **Статус (исторический):** все открытые вопросы §8.16 закрыты. Ниже — подтверждённые факты.
> Таблица `TRIGGER_KINDS` **удалена из кода**; доказательство получено **тостингом сценариев
> в UI**: Alex выключил/включил тестовые сценарии, поле 5 снято до и после, плюс повторный
> тостинг `8597`/`8599` дал **включённые** значения `schedule`/`interval` (`0` и `2`).
## (а) 🔴 Поле 5 типа 11 = **тип сценария** + флаг «выключен». ПОДТВЕРЖДЕНО прибора + Alex
```text
type = field5 & 7 # 0 = manual | schedule, 1 = trigger, 2 = interval
enabled = not (field5 & 8) # бит 8 = сценарий ВЫКЛЮЧЕН
```
> 🔴 **Слово — `type`, не `kind`.** Слова `kind` в конфиге и UI нет; я его выдумал. Alex:
> «какой нахуй kind?? что это блядь значит?! я его откуда тебе высру?!» Типы назвал Alex:
> **`manual`, `trigger`, `interval`, `schedule`**.
| Сценарий | Выкл | Вкл | `type` | Прочие поля |
|---|---|---|---|---|
| `8456` «Простой тестовый» | `8` | — | 0 = `manual` | — |
| `8597` «по расписанию» | `8` | **`0`** | 0 = `schedule` | поля 3/4 = `61`, `3354` |
| `11109` «Передернуть Котельной» | — | `0` | 0 = `manual` | 2 элемента в поле 2 |
| `8547` «по времени» | `9` | — | 1 = `trigger` | — |
| `8551` «по триггеру» | `9` | — | 1 = `trigger` | — |
| `9628` «Автомат.: Рад. ванная 2эт ВКЛ» | `9` | `1` | 1 = `trigger` | — |
| `8599` «по интервалу» | `10` | **`2`** | 2 = `interval` | поле 6 = `43200000` мс |
**🔴 Включённые `0` (8597) и `2` (8599) получены тостингом на приборе:** Alex включил оба
сценария в UI, конфиг снят заново (`curl -s http://192.168.0.50/config.txt`). До этого момента
включённые значения `schedule`/`interval` были неизвестны — были только выключенные `8`/`10`.
**Итог: у каждого типа есть вкл/выкл, и всегда `вкл + 8 = выкл`:**
| тип | вкл | выкл |
|---|---|---|
| `manual` | `0` (11109) | `8` (8456) |
| `schedule` | `0` (8597) | `8` (был) |
| `trigger` | `1` (64 шт) | `9` (8547, 8551) |
| `interval` | `2` (8599) | `10` (был) |
> ⚠️ **`manual` (0) и `schedule` (0) — один и тот же `type`.** Различаются **наличием полей 3/4**
> (`days_mask` + `time`): у расписания они заполнены, у ручного — нули. Alex: «manual от schedule
> очевидно отличаются наличием блядь schedule!»
> Кодировку расписания см. §8.1 (`61` = ПН,СР,ЧТ,ПТ,СБ; `3354` = `(13<<8)|26` = 13:26).
**Почему старая модель `{0:manual,1:manual,8:schedule,9:trigger,10:interval}` была неверна:**
она читала число как «класс запуска» целиком. На самом деле число **двухбитовое по смыслу**:
младшие 3 бита = тип, бит `8` = выключено. Отсюда все противоречия §8.16.
> ❌ **Снятые гипотезы (не возвращаться):** «`field5 & 7` = значение из условия шага 46»
> (32 расхождения из 70 — пары ВКЛ/ВЫКЛ `9628`/`9629` имеют одинаковое поле `1` при
> противоположных условиях); «поле 5 не выводится из содержимого» (выводится: тип + бит 8).
**Что в коде:** `TRIGGER_KINDS`, `_raw_trigger_kind`, `_raw_trigger_params`, `time_raw` **удалены**.
Заголовок сценария в YAML теперь: `enabled`, **`type`**, `days`/`days_mask`, `time`, `interval_ms`.
Служебные `_f5`/`_kind` **тоже убраны** — поле 5 полностью восстанавливается из `enabled` + `type`.
## (б) Форма YAML сценария — ПЕРЕСМОТРЕНА Alex'ом (финал сессии)
Alex отверг `if`/`then`/`else` в YAML как **выдумку**:
> «у тебя в `9819` сейчас в yml написан `if`! а это не `if` а **триггер**»
Проверка конфига подтвердила: слов `if`, `then`, `else`, `condition`, `object`, `operator`, `group`,
`op` **в конфиге нет**. Это была интерпретация парсера поверх данных.
**Корневое требование:** `trigger` — это **триггер**, а не `if`; `steps` — это **шаги**, а не ветвление.
### Согласованная форма (принята Alex)
```yaml
- id: 9691
name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ'
enabled: true
trigger:
- id: 9729
object: '9495'
value: 1
steps:
- id: 9819
action: 9563
```
### Соответствие строкам конфига — `#Z9691` «Прихожая (н/п) 14/14 ВКЛ»
```text
#Z9691=11,'Автомат.: Прихожая (н/п) (14/14) ВКЛ',[9819],0,0,1,0,0
#Z9819=46,0,9729,[9563],[] ← шаг: условие 9729, действие 9563
#Z9729=49,9495,1,1 ← триггер: объект 9495, значение 1
#Z9495=14,'Датч 14/14: Прихожая (н/п)',151408,0
#Z9563=5,'Включить выход 14/14: Прихожая (н/п)',146064,1,0,0,[],0,0,0,512
```
| YAML | Строка конфига | Поля |
|---|---|---|
| `id: 9691`, `name` | `#Z9691=11,…` | 1, 2 |
| `enabled: true` | `#Z9691` поле 5 = `1` | `not (1 & 8)` |
| `trigger[].id: 9729` | `#Z9729=49,…` | 1 (id самого условия) |
| `trigger[].object: '9495'` | `#Z9729` | 2 (id объекта, на который смотрит триггер) |
| `trigger[].value: 1` | `#Z9729` | 4 (значение; поле 3 = оператор) |
| `steps[].id: 9819` | `#Z9819=46,…` | 1 (id шага) |
| `steps[].action: 9563` | `#Z9819` | 4 (первый id из `[9563]`) |
| тело действия | `#Z9563=5,'…',146064,1,…` | живёт своей строкой |
### Терминология Alex — точная
| Id | Что это по Alex | Где в YAML |
|---|---|---|
| `9691` | **сценарий** | `id` верхнего уровня |
| `9729` | **триггер** (не `if`!) | `trigger[].id` |
| `9495` | объект, за которым следит триггер | `trigger[].object` |
| `9819` | **шаг** (`if of the step` по формулировке Alex) | `steps[].id` |
| `9563` | **действие** — «выполнить пользовательское действие» | `steps[].action` |
> Alex: «`#Z9563=5,…` — это шаг «выполнить пользовательское действие» (action) `9563`».
### Единая модель вложенных id
Разница между `trigger` и `steps` — только в том, какое поле `#Z9819` (тип 46) на них указывает:
```text
#Z9819 = 46, 0, 9729, [9563], []
│ │
│ └── steps[].action ← поле 4 (список действий)
└───────── trigger[].id ← поле 3 (условие)
```
`object` выбран словом для ссылки на объект, потому что **в остальном коде уже так**
(`dump_condition` для type 49: `'object': node[1]`), рядом с `target` (реле/контур) и `descr`.
### Следствия для кода — ✅ ПУНКТЫ 1–3, 5 СДЕЛАНЫ (2026-09-17)
| # | Что | Статус |
|---|---|---|
| 1 | `dump_condition` type 49 → `{id, object, value}` вместо `{id, condition: {object, operator, value}}` | ✅ сделано |
| 2 | `dump_step` type 46 → `{id, action: <первый then id>}`; `trigger` **вынимается на уровень сценария** | ✅ сделано |
| 3 | Заголовок сценария — `kind`/`kind_raw`/`_kind`/`_f5` **убраны**, вместо них видимое поле **`type`** | ✅ сделано |
| 4 | Непустые `then`/`else` / второй+ id → что писать в форме | 🔴 **открытый вопрос, ждёт Alex** |
| 5 | `dump_action` — делегирование в `dump_step` (чтобы type 5/9 в телах не падали в `raw`) | ✅ сделано |
| 6 | Энкодер под новую форму | ⏸ **не начат** — ждёт подтверждения формы. Round-trip сломан |
| 7 | `type` для `11109` выходит `trigger` вместо `manual` | ⚠️ **баг**, гипотеза правки: `trigger` = ровно один элемент в `steps` И он типа 46. Прогон не выполнен |
### ✅ Фактический вывод парсера после правок (проверено 2026-09-17)
Перегенерация `zont_config/config_local_2026-09-17_17-45-00.yml` (exit 0, 4405 строк — было 5376):
```yaml
- id: 9691
name: 'Автомат.: Прихожая (н/п) (14/14) ВКЛ'
enabled: true
_kind: 1
_f5: 1
steps:
- id: 9819
trigger:
id: 9729
object: 9495
value: 1
action: 9563
```
`if`/`then`/`else`/`condition`/`operator` из YAML ушли полностью. Три служебных ключа
(`_kind`, `_f5`, `_op`) содержат то, что энкодеру нужно для байт-в-байт сборки:
`_op` пишется **только когда оператор ≠ 1**; `_kind` — только когда `field5 & 7 ≠ 0`; `_f5` — всегда.
**Многошаговый `11109` — форма подтвердила проблему:**
```yaml
- id: 11109
name: Передернуть Автомат Котельной
enabled: true
_f5: 0
steps:
- id: 11827
trigger:
id: 11823
object: 11190
value: 0
action: 11191
_then:
- 11191
- 11030
- 11824
- 11029
- 11825
- 11192
- 11826
- id: 11828
wait: 0
```
🔴 **Это и есть открытый вопрос №4.** У `11109` в `then` семь id — цепочка, а не одно действие.
`action: 11191` показывает только первое, остальные шесть ушли в служебный `_then`. Для 11109
`_then`**не служебный ключ, а реальное содержимое**. Форма «один триггер → одно действие»
описывает 64 из 65 сценариев; 11109 из неё выпадает. Ждём решения Alex: `action` остаётся
одиночным, а цепочка — отдельная тема, или появляется `actions:`. **Правка кода остановлена
на этом вопросе, энкодер не тронут.**
## (в) Методология: почему форма переделывалась дважды
1. **`blocks`/`extra_links`** — отвергнуто: порядок терялся, сценарий превращался в список цифр.
2. **`when`/`then` + `if`/`group`/`op`** — отвергнуто как **выдумка**: слов нет в конфиге.
3. **`trigger`/`steps` + `action`** — принято (эта форма).
> 🔴 **Правило, выведенное из трёх итераций:** в YAML попадают **только слова, которые есть
> в конфиге или в UI**. `object` — есть (здесь так называется ссылка), `descr`/`target`/`value` —
> есть (поля строки). `if`/`then`/`condition`/`operator`/`group`/`op`/`kind` — **нельзя**:
> их в данных нет, это интерпретация.
> 🔴 **Второе правило:** `descr` ставить только там, где текст **реально есть в строке**.
> Для `#Z9819` (шаг) текста нет — значит и `descr` у него быть не должно (Alex: «нахуй там тогда
> descr?! если его нет в оригинале?»).
> ⚠️ **Не забегать вперёд с вопросами о том, чего в кейсе нет.** Вопрос «что писать, когда
> в `trigger` окажется type 47» Alex воспринял как шум («что за type 47? нихуя не понятно») —
> в разбираемом сценарии его нет. Сначала форма на текущем кейсе, потом обобщение.
## (г) Состояние на конец сессии (обновлено 2026-09-17, после правок кода)
| Что | Статус |
|---|---|
| Форма YAML сценария | ✅ согласована (см. (б)) |
| Правка `config-to-yml.py` под форму | ✅ **сделана** — 3 патча (`dump_condition` type 49, `dump_step` type 46, заголовок) |
| YAML перегенерирован | ✅ `config_local_2026-09-17_17-45-00.yml`**4405 строк** (было 5376), `if`/`then` ушли |
| Проверка формы глазами | ✅ блок `9691` и `11109` прочитаны |
| Round-trip | ⏸ **не прогнан** после правок формы (энкодер ещё не подогнан — прогон осмыслен только после пункта 6) |
| Многошаговые сценарии (`11109`) в форме | 🔴 **открытый вопрос №4** — ждёт Alex |
| Энкодер под новую форму | ⏸ не начат |
| `git commit` | ⏸ не закоммичено, ждёт команды |
**Артефакты:**
- `zont_config/config_local_2026-09-17_17-45-00.yml` — актуальный, **перегенерирован** под новую форму (4405 строк)
- `/tmp/backup-174500.yml` — бэкап предыдущей генерации (5376 строк, форма со старым `if`/`then`)
> ⚠️ **Питфолл, найденный в этой сессии:** `config-to-yml.py` пишет YAML **в stdout**, а не в файл.
> `main()` не принимает пути вывода (`sys.argv` = только входной `.txt`). Первый прогон
> `python3 config-to-yml.py f.txt -o /tmp/x.yml` **молча проигнорировал `-o`** — скрипт напечатал
> «Использование: zont_to_yaml.py <config.txt>» и `exit 1`, а YAML остался **старым**. Из-за этого
> первый просмотр блока 9691 показал неисправленную форму и выглядел как «патч не сработал».
> **Правильный вызов:** редирект в файл — `python3 config-to-yml.py in.txt > out.yml`, и всегда
> проверять `exit=` и размер целевого файла.
> ⚠️ **Питфолл (продолжение):** `grep -n "id: 9691"` по YAML находит **несколько** совпадений
> (объект живёт и в своей секции, и внутри сценария). Номер строки в старом файле (4877) и в новом
> (3932) различается — индексировать по первому совпадению нельзя, надо смотреть окрестности.