[2026-09-18] eagle: family/how-to/home-automation.md personal/projects/zont-config-compiler.md personal/tech/roundtrip-key-verification.md
This commit is contained in:
@@ -745,10 +745,17 @@ ZONT ──[тип 51: устройство, slave_id]────────
|
||||
только контур отопления `#Z10152=16,'Тёплый пол'` и его штатный датчик `#Z8432=27`. Ни одной записи
|
||||
типа 51/1 для тёплых полов нет → bridge их отдаёт, ZONT **не спрашивает**.
|
||||
|
||||
**⏳ Состояние регистрации (2026-09-18, круг 51):** работа **в процессе**. Энкодер научился
|
||||
выдавать авто-id для типов **1 / 51 / 52** (коммит `9dfb2ed`), поэтому 21 новый объект
|
||||
(7 × тип 51 + 7 × тип 52 + 7 × тип 1) добавляются **без прописанных id**. Имена на ZONT-стороне —
|
||||
**короткий префикс «ТП: »** (`ТП: гостиная`, `ТП: серая`, …), решение Alex, не полная фраза
|
||||
«Температура … тёплый пол». Скрипт `/tmp/add_floor_sensors.py` (флаг `--toilet` для 8-го датчика,
|
||||
slave 112) готов и **не запущен** — ждёт ответа Alex по slave 112. Детали: §32 доки компилятора.
|
||||
|
||||
**Порядок работ (обе стороны!).** Bridge-сторона и ZONT-сторона правятся **разными файлами** и
|
||||
деплоятся по-разному: bridge — `/addons/modbus-bridge/data/config.template.tmpl` на t610 + `rebuild`;
|
||||
ZONT — конвертеры в `/Users/admin/Automation/HA-ZONT-Modbus` + заливка на контроллер.
|
||||
⚙️ Механика ZONT-стороны: [[personal/projects/zont-config-compiler]] §31.
|
||||
⚙️ Механика ZONT-стороны: [[personal/projects/zont-config-compiler]] §31, §32.
|
||||
|
||||
### AT2 (slave 10) — вентиляторы
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: ⚙️ ZONT Config Compiler — конвертеры .txt ⇄ .yml
|
||||
namespace: personal
|
||||
type: how-to
|
||||
created: '2026-09-17'
|
||||
updated: '2026-09-18 (круг 51: Zigbee тёплый пол → Modbus, решения Alex + замер авто-id для 51/52/1)'
|
||||
updated: '2026-09-18 (круг 51: авто-id для типов 1/51/52 + липкий номер (питфолл 107), коммит 9dfb2ed)'
|
||||
tags:
|
||||
- personal
|
||||
- zont
|
||||
@@ -40,6 +40,24 @@ aliases:
|
||||
- ZONT keep renames питфолл
|
||||
- ZONT барьер id in obj
|
||||
- ZONT питфолл 104
|
||||
- ZONT круг 51
|
||||
- ZONT авто-id 1 51 52
|
||||
- ZONT modbus_devices без id
|
||||
- ZONT virtual_sensor без id
|
||||
- ZONT вложенный регистр auto id
|
||||
- ZONT питфолл 106
|
||||
- ZONT питфолл 107
|
||||
- ZONT липкий id
|
||||
- ZONT keep sticky id
|
||||
- ZONT alloc None не кэширует
|
||||
- ZONT двойной id объект пропадает
|
||||
- ZONT круг чистый не значит выпущен
|
||||
- ZONT ТП префикс
|
||||
- ZONT тёплый пол modbus
|
||||
- ZONT тест_autoid_granica2
|
||||
- ZONT accept_autoid_types
|
||||
- ZONT add_floor_sensors
|
||||
- ZONT _alloc_id два прохода
|
||||
- ZONT валидация ссылок
|
||||
- ZONT ссылка в никуда
|
||||
- ZONT конец сценария
|
||||
@@ -174,17 +192,18 @@ related:
|
||||
| **Снять живой конфиг** | 🔴 `curl -s http://192.168.0.50/config.txt` — **без авторизации** |
|
||||
| **ZONT в общем контуре** | [[family/how-to/home-automation]] §6 |
|
||||
|
||||
> ### 📍 Текущее состояние (2026-09-18, после §31)
|
||||
> ### 📍 Текущее состояние (2026-09-18, после §32 = круг 51)
|
||||
>
|
||||
> | | |
|
||||
> |---|---|
|
||||
> | **HEAD** | `7e34ee1` — «авто-id для листьев + валидация ссылок (круг 50)» |
|
||||
> | **HEAD** | `9dfb2ed` — «авто-id: типы 1/51/52 (белый список + липкий номер)» |
|
||||
> | **Круг** | **10/10 зелёный**, 759 → 759 объектов |
|
||||
> | **Артефакт** | `zont_config/config_local_2026-09-18_00-45-31.yml` (идентичен `23-21-20`) |
|
||||
> | **Свежий дамп** | ✅ снят 2026-09-18 00:45 — **идентичен** `23-21-20` (`diff` пуст) |
|
||||
> | **Открыто** | 🔴 **§31: 7 Zigbee-датчиков тёплых полов → slave'ы 105–111.** Решения Alex получены (авто-id, имена в стиле ZONT), замер показал: типы 51/52/1 требуют снятия барьеров `'id'`. Bridge-правка НЕ нужна. Ждёт 1 ответ: добавлять ли slave 112. · авто-id: верх окна не назван (§30.3) |
|
||||
> | **✅ Готово** | Авто-id для типов **1 / 51 / 52** (`_LEAF_TYPES = {9, 1, 51}` + снятие барьеров `'id'` + **липкий номер** через `keep()`, питфолл 107). Приёмка зелёная, регресс 10/10 |
|
||||
> | **Открыто** | 🔴 **§31: 7 Zigbee-датчиков тёплых полов → slave'ы 105–111.** Скрипт `/tmp/add_floor_sensors.py` готов, **не запущен** — ждёт 1 ответ: добавлять ли slave 112 (туалет 1). · авто-id: верх окна не назван (§30.3, стоит `19999`) |
|
||||
>
|
||||
> Свежие круги: §27 (46 — `set_contour_target`), §28 (47–49 — `call_sub`, `send_sms`, `exit`, `scenario_orphans`), §30 (50 — авто-id + валидация ссылок), §31 (Zigbee → Modbus).
|
||||
> Свежие круги: §27 (46 — `set_contour_target`), §28 (47–49 — `call_sub`, `send_sms`, `exit`, `scenario_orphans`), §30 (50 — авто-id + валидация ссылок), §31 (разведка Zigbee → Modbus), §32 (**51 — авто-id типов 1/51/52**).
|
||||
>
|
||||
> ✅ Снято с открытого в §28.6: «5 `objcmd`-орфанов» и «поле 4 = `2` у `expr`» — **работы не требуют** (орфанов нет; поле производное).
|
||||
|
||||
@@ -4544,43 +4563,69 @@ ssh root@192.168.2.176 'grep -nE "floor_temperature" \
|
||||
|---|---|---|
|
||||
| **1** | Снять + сконвертировать свежий конфиг, круг по всем снапшотам | ✅ **сделано**: 10/10 зелёный, `config_local_2026-09-18_00-45-31.{txt,yml}`, YAML идентичен `23-21-20` |
|
||||
| ~~2~~ | ~~Bridge: добавить 7 маппингов~~ | ✅ **не требуется** — уже есть (§31.6) |
|
||||
| **3** | Энкодер: расширить `_LEAF_TYPES` до `{9, 1, 51}` + снять барьеры `'id'` в 4 точках (§31.5) | ❌ не начато |
|
||||
| **4** | ZONT YAML: +21 объект — 7 × тип 51 (slave 105–111) + 7 × тип 52 (вложенные) + 7 × тип 1 | ❌ не начато |
|
||||
| **3** | Энкодер: расширить `_LEAF_TYPES` до `{9, 1, 51}` + снять барьеры `'id'` в 4 точках (§31.5) | ✅ **сделано**, коммит `9dfb2ed` (§31.9) |
|
||||
| **4** | ZONT YAML: +21 объект — 7 × тип 51 (slave 105–111) + 7 × тип 52 (вложенные) + 7 × тип 1 | ⏳ скрипт готов, ждёт ответа по slave 112 |
|
||||
| **5** | Энкодер → txt, круг зелёный, показать Alex'у 21 строку с выданными id | ❌ не начато |
|
||||
| **6** | **Проверка Alex'ом в UI ZONT** → заливка → коммит | ❌ не начато |
|
||||
|
||||
**Имена (стиль существующих, решение Alex):**
|
||||
**Имена (префикс «ТП: », решение Alex 2026-09-18):**
|
||||
|
||||
| Slave | HA-сущность | Имя в ZONT |
|
||||
|---|---|---|
|
||||
| 105 | `sensor.living_room_floor_temperature_temperature` | Температура гостиная тёплый пол |
|
||||
| 106 | `sensor.severnaia_floor_temperature_temperature` | Температура серая тёплый пол |
|
||||
| 107 | `sensor.kabinet_floor_temperature_temperature` | Температура кабинет тёплый пол |
|
||||
| 108 | `sensor.kitchen_floor_temperature_temperature` | Температура кухня тёплый пол |
|
||||
| 109 | `sensor.vannaia_floor_temperature_temperature` | Температура ванная тёплый пол |
|
||||
| 110 | `sensor.prikhozhaia_floor_temperature_temperature` | Температура прихожая тёплый пол |
|
||||
| 111 | `sensor.dushevaia_floor_temperature_temperature` | Температура душевая тёплый пол |
|
||||
| 105 | `sensor.living_room_floor_temperature_temperature` | ТП: гостиная |
|
||||
| 106 | `sensor.severnaia_floor_temperature_temperature` | ТП: серая |
|
||||
| 107 | `sensor.kabinet_floor_temperature_temperature` | ТП: кабинет |
|
||||
| 108 | `sensor.kitchen_floor_temperature_temperature` | ТП: кухня |
|
||||
| 109 | `sensor.vannaia_floor_temperature_temperature` | ТП: ванная |
|
||||
| 110 | `sensor.prikhozhaia_floor_temperature_temperature` | ТП: прихожая |
|
||||
| 111 | `sensor.dushevaia_floor_temperature_temperature` | ТП: душевая |
|
||||
| 112? | `sensor.toilet_1_floor_temperature_temperature` | ТП: туалет 1 (под вопросом) |
|
||||
|
||||
> 🔴 **Alex отверг длинный вариант имён.** Первый мой вариант был «Температура гостиная тёплый
|
||||
> пол» — Alex ответил: **«Имена: префикс \"ТП: \"»**. Короткий префикс, не полная фраза. Формулировка
|
||||
> «в стиле существующих на зонте» означала **тот же короткий регистр**, а не копирование шаблона
|
||||
> `Температура <место>`. Урок: «в стиле существующих» ≠ «повтори существующий текст целиком».
|
||||
|
||||
Форма каждого объекта (зеркало `#Z8381`/`#Z8383`/`#Z8382` из §31.2):
|
||||
|
||||
```yaml
|
||||
modbus_devices:
|
||||
- slave_id: 105
|
||||
name: Температура гостиная тёплый пол
|
||||
name: 'ТП: гостиная'
|
||||
poll_interval: 5000
|
||||
timeout: 300000
|
||||
registers:[{address: 100, function: holding_3_6, bit_width: 16,
|
||||
signal_type: param_int16, direction: read}]
|
||||
registers:
|
||||
- name: 'Знач. темп.'
|
||||
address: 100
|
||||
function: holding_3_6
|
||||
bit_width: 16
|
||||
signal_type: param_int16
|
||||
direction: read
|
||||
raw_params: [[], [], 0]
|
||||
virtual_sensors:
|
||||
- serial_number: '0'
|
||||
name: Температура гостиная тёплый пол
|
||||
name: 'ТП: гостиная'
|
||||
register_id: <id выданного регистра>
|
||||
connection_loss_delay_ms: 60000
|
||||
```
|
||||
|
||||
⏳ **Один открытый вопрос:** slave **112** (`toilet_1_floor_temperature_temperature`) — мост его уже
|
||||
отдаёт, но в план Alex'а он не входил. Добавлять 8-й датчик или оставить на потом?
|
||||
**ID — по решению Alex «Let allocator decide»:** ни один id в YAML **не прописывается**.
|
||||
Все 21 объект идут без `id`, номера выдаёт аллокатор из окна `4098..19999` (дыры снизу вверх).
|
||||
Регистр и виртуальный датчик ссылаются на выданный номер автоматически (`register_id`
|
||||
подставляет проход устройства, см. §31.9).
|
||||
|
||||
**Скрипт добавления (идемпотентный, флаг `--toilet` для 8-го датчика):**
|
||||
|
||||
```bash
|
||||
python3 /tmp/add_floor_sensors.py # 7 датчиков (105–111)
|
||||
python3 /tmp/add_floor_sensors.py --toilet # 8 датчиков (+112)
|
||||
```
|
||||
|
||||
⏳ **Один открытый вопрос (единственный):** slave **112**
|
||||
(`toilet_1_floor_temperature_temperature`) — мост его уже отдаёт, но в план Alex'а он не входил.
|
||||
Добавлять 8-й датчик или оставить на потом? Alex этот вопрос ещё не разобрал —
|
||||
на повторную формулировку ответил «Второй вопрос не понял», поэтому вопрос переформулирован
|
||||
в одну строку.
|
||||
|
||||
### 31.8. Статус
|
||||
|
||||
@@ -4588,11 +4633,148 @@ virtual_sensors:
|
||||
|---|---|
|
||||
| Свежий конфиг снят, конвертирован | ✅ `config_local_2026-09-18_00-45-31.{txt,yml}` — идентичен `23-21-20`, круг **10/10** |
|
||||
| Разведка (11 slaves, 7 отсутствующих датчиков) | ✅ выполнена, факты §31.2–31.3 |
|
||||
| Решения Alex | ✅ оба получены: авто-id + имена в стиле ZONT (§31.4) |
|
||||
| Замер авто-id для 51/52/1 | ✅ выполнен — **все три типа падают** `Ошибка: 'id'` (§31.5) |
|
||||
| Решения Alex | ✅ авто-id («Let allocator decide») + имена с префиксом «ТП: » (§31.4, §31.7) |
|
||||
| Замер авто-id для 51/52/1 | ✅ выполнен — **все три типа падали** `Ошибка: 'id'` (§31.5) |
|
||||
| Bridge | ✅ правка НЕ нужна — slaves 105–112 уже в шаблоне (§31.6) |
|
||||
| План | ✅ уточнён: 6 фаз, фаза 1 закрыта (§31.7) |
|
||||
| Правки кода / конфига | ❌ не начаты (правило: план прежде кода) |
|
||||
| HEAD | `7e34ee1` (авто-id листьев + валидация ссылок) |
|
||||
| **Энкодер: авто-id для 1/51/52** | ✅ **сделано, коммит `9dfb2ed`** — приёмка зелёная, регресс 10/10 (§31.9) |
|
||||
| Правка ZONT YAML (+21 объект) | ⏳ скрипт `/tmp/add_floor_sensors.py` готов, не запущен — ждёт ответа по slave 112 |
|
||||
| HEAD | `9dfb2ed` (авто-id типов 1/51/52) |
|
||||
|
||||
---
|
||||
|
||||
## 32. ✅ Круг 51 (2026-09-18) — авто-id для типов 1 / 51 / 52, липкий номер
|
||||
|
||||
**Задача (Alex):** «Тяни и конвертируй свежий конфиг и добавь в него entity zigbee датчиков
|
||||
температуры которых в нем нет через виртуальные modbus устройства в bridge».
|
||||
|
||||
**Решения Alex по ходу:**
|
||||
1. Способ выдачи id — **«Let allocator decide»** (не прописывать номера руками).
|
||||
2. Имена — **префикс «ТП: »**.
|
||||
|
||||
### 32.1. Что сделано
|
||||
|
||||
| # | Шаг | Статус |
|
||||
|---|---|---|
|
||||
| 1 | Свежий конфиг: `curl` → `config-to-yml.py` → целевой `.yml` | ✅ 10/10 круг, YAML идентичен `23-21-20` |
|
||||
| 2 | Разведка bridge | ✅ slaves 105–112 **уже есть**, правка не нужна |
|
||||
| 3 | Замер авто-id 51/52/1 | ✅ все три падали `Ошибка: 'id'` (питфолл 104 в новых местах) |
|
||||
| 4 | `_LEAF_TYPES = {9}` → `{9, 1, 51}` | ✅ |
|
||||
| 5 | Снятие барьеров `obj['id']` в 4 точках | ✅ |
|
||||
| 6 | 🔴 **Найден и исправлен баг «двойного id»** — см. §32.3 | ✅ |
|
||||
| 7 | Приёмка: 3 типа, id в окне, круг чистый | ✅ `/tmp/accept_autoid_types.py` |
|
||||
| 8 | Регресс по 10 снапшотам | ✅ **10/10** |
|
||||
| 9 | Коммит | ✅ `9dfb2ed` |
|
||||
|
||||
**Правки энкодера (идемпотентный скрипт `/tmp/fix_autoid_types.py`, 5 замен с `count == 1`):**
|
||||
|
||||
| Место | Что |
|
||||
|---|---|
|
||||
| `_LEAF_TYPES` | `{9}` → `{9, 1, 51}`; тип 52 — вложенный, в список не идёт |
|
||||
| `virtual_sensors` (~329) | `if 'id' not in obj` → выдача номера |
|
||||
| `modbus_devices` (~1776) | выдача id вложенным регистрам **до** сбора `register_ids` |
|
||||
| текст ошибки устройства | `obj['id']` → `obj.get('id')` |
|
||||
| сборка строки устройства | `if 'id' not in obj` → выдача номера |
|
||||
|
||||
### 32.2. 🔴 ПИТФОЛЛ 106 — «круг чистый» НЕ значит «объект выпущен»
|
||||
|
||||
Промежуточная приёмка показывала `exit=0` и `roundtrip: ЧИСТЫЙ` для всех трёх типов —
|
||||
**при том, что объект молча исчезал из вывода**. Круг проверяет «вывод парсера == вход»,
|
||||
а входом был вывод энкодера, из которого объект уже пропал. Зелёный круг на потере объекта.
|
||||
|
||||
**Как ловить:** считать объекты **до и после** (`len(in) vs count(out)`) и искать объект по
|
||||
имени в артефакте. Признак: `exit=0`, круг чистый, а строки нет.
|
||||
|
||||
```python
|
||||
# правильная проверка — по имени, а не по «круг чистый»
|
||||
hits = [l for l in out.split("\r\n") if "ТЕСТ-ДАТЧИК" in l]
|
||||
if not hits: print("ОБЪЕКТ ПОТЕРЯН")
|
||||
```
|
||||
|
||||
### 32.3. 🔴 ПИТФОЛЛ 107 — `_alloc.alloc(None)` НЕ кэширует → два id на один объект
|
||||
|
||||
**Первопричина потери объекта** (найдена трассировкой, не догадкой).
|
||||
|
||||
`_IdAllocator.alloc(old_id=None)` кэширует выдачу в `self.renames` **только если
|
||||
`old_id is not None`** (строка 63). Вызов `_alloc.alloc(None)` номер **не запоминает**.
|
||||
|
||||
Энкодер проходит объекты **дважды**:
|
||||
1. секционный цикл (`virtual_sensors` ~329, `modbus_devices` ~1776) — кладёт строку в `lines`;
|
||||
2. цикл `TYPE_ORDER` (~2118) — собирает `result_lines`, ища `zid in z_dict`.
|
||||
|
||||
В проходе 1 объект без id получал номер **A**. В проходе 2 `_alloc_id(<int>)` видел число, которого
|
||||
нет ни в `_known_ids`, ни в `renames` → вызывал `alloc(<int>)` → выдавал **новый номер B**.
|
||||
`B in z_dict` → `False` → строка **молча** терялась.
|
||||
|
||||
**Доказательство трассировкой** (временные принты в копию энкодера):
|
||||
|
||||
```
|
||||
DBG-T1 [..., "#Z4102=1,'0','ТЕСТ-ДАТЧИК',..."] ← проход 1 выдал 4102
|
||||
DBG-TZ obj_id=4101 -> zid=4101 in_z_dict=False ← проход 2 выдал 4101, строки нет
|
||||
```
|
||||
|
||||
**Лечение:** номер делается **липким** через `_alloc.keep(new_id)` сразу после выдачи —
|
||||
`keep()` регистрирует тождественное переименование `id -> id`, и любой последующий
|
||||
`_alloc_id(<int>)` возвращает тот же номер (ветка `zid in _alloc.renames`).
|
||||
|
||||
```python
|
||||
if 'id' not in obj:
|
||||
obj['id'] = _alloc.alloc(None)
|
||||
_alloc.keep(obj['id']) # ← без этой строки объект пропадёт
|
||||
```
|
||||
|
||||
Это **та же механика, что спасла 66 шагов типа 46 в круге 50** (см. docstring `keep()`):
|
||||
там отсутствие `keep` дало «строку в никуда». Питфолл повторяется — при любой новой точке
|
||||
авто-id `keep()` обязателен.
|
||||
|
||||
**Скрипт правки:** `/tmp/fix_autoid_sticky.py` (3 замены, `count == 1`).
|
||||
|
||||
### 32.4. ✅ Приёмка (проверено, не «должно работать»)
|
||||
|
||||
```
|
||||
=== A: тип 51 (modbus_device) без id ===
|
||||
51 exit=0 id=4101 in_window=True
|
||||
#Z4101=51,250,'ТЕСТ-ДЕВ',5000,300000,[],[],0,[]
|
||||
roundtrip: ЧИСТЫЙ
|
||||
=== B: тип 52 (вложенный регистр) без id ===
|
||||
52 exit=0 id=4101 in_window=True
|
||||
#Z4101=52,'ТЕСТ-РЕГ',100,16,0,1,16,0,1,29,1,4
|
||||
roundtrip: ЧИСТЫЙ
|
||||
=== C: тип 1 (virtual_sensor) без id ===
|
||||
1 exit=0 id=4101 in_window=True
|
||||
#Z4101=1,'0','ТЕСТ-ДАТ',0,0,0,60000,[],[],[],8383,[],0,0,0
|
||||
roundtrip: ЧИСТЫЙ
|
||||
=== ИТОГ: ВСЁ ЗЕЛЁНОЕ ===
|
||||
```
|
||||
|
||||
**Проверка ссылки устройство→регистр** (оба без id):
|
||||
|
||||
```
|
||||
#Z4102=51,252,'ТЕСТ-ПАРА',5000,300000,[],[],0,[4101]
|
||||
#Z4101=52,'ТЕСТ-РЕГ2',100,16,0,1,16,0,1,29,1,4
|
||||
>>> device id=4102, register id=4101, device refs [4101] -> LINK OK=True
|
||||
```
|
||||
|
||||
Ссылка подставляется верно — критично, иначе ZONT опрашивал бы регистр по неверному id.
|
||||
|
||||
**Регресс:** 10/10 снапшотов `ЧИСТЫЙ`.
|
||||
|
||||
**Инструменты:** `/tmp/fix_autoid_types.py`, `/tmp/fix_autoid_sticky.py` (правки, идемпотентные),
|
||||
`/tmp/accept_autoid_types.py` (приёмка), `/tmp/trace_zdict.py`, `/tmp/trace_typeorder.py`,
|
||||
`/tmp/trace_iter.py`, `/tmp/trace_vs.py` (трассировки). Все в `/tmp`, в репо не кладутся.
|
||||
|
||||
### 32.5. Границы расширения (обновлено)
|
||||
|
||||
| Тип | Секция | Авто-id | Почему |
|
||||
|---|---|---|---|
|
||||
| 9 | `relay_commands` | ✅ | лист, проверено в круге 50 |
|
||||
| **1** | `virtual_sensors` | ✅ | **круг 51** |
|
||||
| **51** | `modbus_devices` | ✅ | **круг 51** |
|
||||
| **52** | `modbus_devices[].registers` | ✅ | **круг 51**, выдаётся в проходе устройства (не через `_LEAF_TYPES`) |
|
||||
| 16, 27 | `heating_circuits`, `temperature_sensors` | ❌ | ссылки **по типу** — нужна правка `_body_type` |
|
||||
| 5, 57, 10 | `actions`, `mqtt_topics`, `gui_switches` | ❌ | цикл требует `id` у каждой записи |
|
||||
|
||||
**Правило:** каждый новый тип обязан пройти `/tmp/accept_autoid_types.py`-подобный замер —
|
||||
«снял id → собрал → объект есть в выводе → круг чистый». Питфолл 106/107 показывает, что
|
||||
«круг чистый» сам по себе **не доказательство**.
|
||||
|
||||
|
||||
|
||||
@@ -320,3 +320,55 @@ git stash pop
|
||||
|
||||
База тоже сломана → унаследовано; база зелёная → это твоя правка. И **коммитить сразу, как
|
||||
только круг зелёный** — `git checkout --` затирает незакоммиченное без возврата.
|
||||
|
||||
### Ловушка 6 — 🔴 «круг чистый» ≠ «объект выпущен» (2026-09-18, круг 51)
|
||||
|
||||
Самая коварная из ловушек: **приёмка показала `exit=0` и `roundtrip: ЧИСТЫЙ` для всех трёх
|
||||
проверяемых типов — при том, что объект молча исчезал из вывода.** Круг проверяет «вывод
|
||||
парсера == вход», а входом был вывод энкодера, из которого объект уже пропал. Зелёный круг
|
||||
на потере объекта.
|
||||
|
||||
**Как ловить — считать объект по ИМЕНИ в артефакте, а не полагаться на круг:**
|
||||
|
||||
```python
|
||||
hits = [l for l in out.split("\r\n") if "ТЕСТ-ДАТЧИК" in l]
|
||||
if not hits:
|
||||
print("ОБЪЕКТ ПОТЕРЯН") # круг при этом может быть «ЧИСТЫЙ»
|
||||
```
|
||||
|
||||
### Ловушка 6.1 — первопричина: `alloc(None)` не кэширует → два id на один объект
|
||||
|
||||
Энкодер проходит объекты **дважды**: секционный цикл кладёт строки в `lines`, затем цикл
|
||||
`TYPE_ORDER` собирает `result_lines`, ища `zid in z_dict`. Если выдача номера **не кэшируется**,
|
||||
второй проход выдаёт **другой** номер, `zid` не находится в `z_dict` и строка теряется молча.
|
||||
|
||||
Доказательство — временные принты в копию энкодера:
|
||||
|
||||
```
|
||||
DBG-T1 [..., "#Z4102=1,'0','ТЕСТ-ДАТЧИК',..."] ← проход 1 выдал 4102
|
||||
DBG-TZ obj_id=4101 -> zid=4101 in_z_dict=False ← проход 2 выдал 4101, строки нет
|
||||
```
|
||||
|
||||
**Лечение — номер должен быть «липким»:** сразу после выдачи зарегистрировать его
|
||||
тождественным переименованием (`keep()`), чтобы любой последующий `_alloc_id(<int>)` вернул
|
||||
тот же номер.
|
||||
|
||||
```python
|
||||
if 'id' not in obj:
|
||||
obj['id'] = _alloc.alloc(None)
|
||||
_alloc.keep(obj['id']) # ← без этой строки объект пропадёт
|
||||
```
|
||||
|
||||
**Урок:** при внедрении авто-id проверять, сколько раз энкодер проходит один объект, и
|
||||
кэшируется ли выдача между проходами. «Круг чистый» этого не покажет (см. ловушку 6).
|
||||
|
||||
**Приёмка авто-id — полный чек-лист** (все пункты обязательны, ни один не заменяет другой):
|
||||
|
||||
| # | Проверка | Что ловит |
|
||||
|---|---|---|
|
||||
| 1 | `exit=0` | падение кодирования |
|
||||
| 2 | объект найден в выводе **по имени** | потерю объекта (ловушки 1, 2, 6) |
|
||||
| 3 | выданный id **внутри окна** `ID_MIN..ID_MAX` | прыжок в железный диапазон |
|
||||
| 4 | ссылка владельца → id ребёнка (устройство → регистр) | битую связь |
|
||||
| 5 | круг чистый на **всём** файле | регресс |
|
||||
| 6 | круг по **всем** снапшотам, не по свежему | регресс на старых дампах |
|
||||
|
||||
Reference in New Issue
Block a user