247 lines
16 KiB
Markdown
247 lines
16 KiB
Markdown
# 8.17. ✅ МОДЕЛЬ ЗАКРЫТА (2026-09-17, финал) — поле 5, форма сценария, type 47/49
|
||
|
||
> **Статус:** все открытые вопросы §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) различается — индексировать по первому совпадению нельзя, надо смотреть окрестности.
|