[2026-08-25] eagle: family/how-to/zont-modbus-bridge-udev-race-protection.md family/plans/zont-modbus-bridge-udev-race-protection.md
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
---
|
||||
status: implemented
|
||||
tags:
|
||||
- family
|
||||
- homeautomation
|
||||
- zont
|
||||
- modbus
|
||||
title: Защита modbus-bridge от гонки с udev (ZONT датчики)
|
||||
updated: '2026-08-25'
|
||||
---
|
||||
# Защита modbus-bridge/mbusd от гонки с udev (ZONT датчики)
|
||||
|
||||
> Статус: **внедрено** (2026-08-25, шаги 1–3). Датчики в ZONT снова отвечают, скрипт и init script на месте.
|
||||
|
||||
## Проблема
|
||||
ZONT (192.168.0.10) — Modbus master на RS-485 шине `ttyZONT`. 485-датчики: Dining=1, Kids=2, Bedroom=3.
|
||||
Контейнер `modbus-bridge` сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики. Если modbus-bridge не слушает шину → **ZONT показывает датчики «недоступными»**.
|
||||
|
||||
**Первопричина (2026-08-25):** гонка загрузки TrueNAS — docker стартовал раньше udev, `/dev/ttyZONT` и `/dev/ttyVent` ещё не существовали → контейнеры `modbus-bridge` (Exit 128) и `mbusd` (Exit 255) упали на старте:
|
||||
`error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (RestartCount=0).
|
||||
Фикс был: ручной `docker start modbus-bridge mbusd`.
|
||||
|
||||
## Почему контейнеры сами НЕ поднялись (RestartCount=0)
|
||||
`restart: unless-stopped`/`always` ретраит в ДВУХ случаях:
|
||||
1. Процесс контейнера **стартовал и завершился** с ненулевым кодом → демон ретраит с backoff.
|
||||
2. **Перезапуск docker daemon** → демон пробует поднять `unless-stopped`/`always` контейнеры.
|
||||
|
||||
Наш случай — ТРЕТИЙ, особый: контейнер **вообще не стартовал** — ошибка на этапе монтирования устройства.
|
||||
- Нет процесса, который «завершился бы» → **restart policy не на что применять** → `RestartCount=0`, демон не ретраит.
|
||||
- После падения (13:24) демон не перезапускался → второго триггера не было.
|
||||
**Вывод:** отказ на этапе mount устройства НЕ перезапускается restart policy. Авто-подъём гарантирован только скриптом, ждущим устройства.
|
||||
|
||||
## Ключевое ограничение (задержка в entrypoint НЕ помогает)
|
||||
У обоих контейнеров устройство проброшено через `devices: /dev/ttyZONT:/dev/ttyUSB0` / `devices: /dev/ttyVent:/dev/ttyUSB0`.
|
||||
**Docker при `start` монтирует устройство ДО запуска процесса** — если `<src>` отсутствует на момент старта, контейнер вообще не стартует, и `sleep` внутри entrypoint/CMD **не выполняется**. Поэтому решение — на уровне init скрипта.
|
||||
|
||||
## Внедрённое решение
|
||||
|
||||
### 1. Скрипт `/mnt/RED_2TB/system/start-modbus.sh` (root, `-rw-r--r--`)
|
||||
Ожидает появления tty-алиасов от udev (до ~50с), затем запускает контейнеры:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Ждём tty-алиасы от udev (макс ~25с), затем стартуем modbus контейнеры.
|
||||
for i in $(seq 1 50); do
|
||||
[ -e /dev/ttyZONT ] && [ -e /dev/ttyVent ] && break
|
||||
sleep 1
|
||||
done
|
||||
sleep 2
|
||||
docker start modbus-bridge mbusd 2>/dev/null
|
||||
```
|
||||
|
||||
### 2. Init script (TrueNAS, id=3)
|
||||
- **when:** POSTINIT, **type:** COMMAND, **timeout:** 30, **enabled:** true
|
||||
- **command:** `bash /mnt/RED_2TB/system/start-modbus.sh`
|
||||
- **comment:** `Start modbus-bridge/mbusd after udev tty aliases`
|
||||
|
||||
### Порядок POSTINIT скриптов
|
||||
| id | command | comment | enabled |
|
||||
|----|---------|---------|---------|
|
||||
| 1 | `cp .../99-tty-alias.rules /etc/udev/rules.d/ && udevadm control --reload-rules && udevadm trigger` | Map ttyUSB | ✅ |
|
||||
| 2 | `bash /mnt/RED_2TB/system/tunnel.sh &` | — | ✅ |
|
||||
| 3 | `bash /mnt/RED_2TB/system/start-modbus.sh` | Start modbus-bridge/mbusd after udev tty aliases | ✅ |
|
||||
|
||||
## Проверка
|
||||
- `/dev/ttyZONT` → `ttyUSB1` (CH340), `/dev/ttyVent` → `ttyUSB0` — симлинки появляются после POSTINIT id=1.
|
||||
- После reboot: `docker ps` — modbus-bridge и mbusd Up; датчики в ZONT отвечают.
|
||||
- Просмотр init scripts: `midclt call initshutdownscript.query`
|
||||
- Откат: `midclt call initshutdownscript.delete <ID>` + удалить скрипт с диска.
|
||||
|
||||
## Связанные заметки
|
||||
- [[family/how-to/home-automation.md]] — карта slave ID, ZONT, датчики
|
||||
- [[family/how-to/truenas-infrastructure.md]] — docker, modbus-bridge, mbusd, udev rules, init scripts
|
||||
Reference in New Issue
Block a user