Skip to content

Architecture

EvelRus edited this page Mar 4, 2026 · 1 revision

🏗 Архитектура

Обзор модулей

┌─────────────────────────────────────────────────┐
│                  Браузер (PWA)                  │
│  public/index.html  ·  css/style.css  ·  js/app.js │
│        Socket.IO client  ·  Web Audio API       │
└──────────────────────┬──────────────────────────┘
                       │ WebSocket / HTTP
┌──────────────────────▼──────────────────────────┐
│                  server.js                       │
│  Express (HTTP API)  ·  Socket.IO Server         │
│  Обнаружение хабов   ·  Маршрутизация событий   │
└────┬──────────────┬──────────────┬───────────────┘
     │              │              │
┌────▼───┐   ┌──────▼──────┐  ┌───▼──────┐
│ramp.js │   │scheduler.js │  │logger.js │
│Разгон  │   │Сценарии     │  │Лог:      │
│Стоп    │   │Расписание   │  │память    │
│LED     │   │Составы      │  │файл      │
│Keepalive│  └─────────────┘  │Socket.IO │
└────┬───┘                    └──────────┘
     │ BLE команды
┌────▼───────────────────────────────────┐
│          node-poweredup / noble        │
│  PoweredUP хабы  ·  PyBricks хабы     │
└────────────────────────────────────────┘

Поток команды setSpeed

Браузер: socket.emit("setSpeed", {trainId, speed})
    ↓
server.js: socket.on("setSpeed") — валидация, дедупликация
    ↓
sched.recordStep() — если идёт запись сценария
    ↓
ramp.stopNow() или ramp.rampTo()  ← единая точка управления скоростью
    ↓
RampEngine._write(trainId, speed)  ← единая точка записи в BLE
    ↓
motor.setPower(speed)  — BLE команда на хаб
    ↓
io.emit("speedUpdate", {trainId, speed})  — обновление UI
    ↓
sched.onTrainSpeedChange()  — обновление состава в UI

Единая точка управления скоростью

Все источники команд (браузер, сценарий, расписание, состав) проходят через одну цепочку:

Источник Вызов
Браузер (ползунок/кнопки) socket.on("setSpeed")ramp.rampTo() / ramp.stopNow()
Состав sched.setConsistSpeed()ramp.rampTo() / ramp.stopNow()
Сценарий scheduler._playingramp.rampTo() / ramp.stopNow()
Расписание scheduler._execAction()ramp.rampTo() / ramp.stopNow()
E-STOP socket.on("estop")ramp.stopAll()ramp.stopNow() для каждого

ramp._write(trainId, speed) — финальная точка, единственное место где происходит реальная BLE-запись.


Хранилище данных

Файл Формат Описание
hub-config.json JSON Настройки хабов: имя, фото, звуки, параметры рампа
data/scenarios.json JSON Сценарии с шагами и задержками
data/schedules.json JSON Задачи расписания
data/consists.json JSON Составы поездов
data/logs/YYYY-MM-DD.log Текст Логи (ротация по суткам)

Жизненный цикл хаба

BLE scan → discover event
    ↓
isPyBricksPeripheral()?
    ├── да → connectPyBricksHub() → PyBricksHub.connect()
    └── нет → connectHub() → hub.connect()
         ↓
Обнаружение моторов и датчиков на портах A-D
         ↓
trains[uuid] = { hub, motor, speed:0, ... }
         ↓
ramp.startKeepalive(uuid)
         ↓
io.emit("newTrain", trainPayload)  → карточка в браузере
         ↓
[работа] hub events: batteryLevel, button, error
         ↓
disconnect event
    ↓
ramp.clearRamp(), ramp.stopKeepalive()
trains[uuid].connected = false
io.emit("hubStatus", { connected: false })
setTimeout(() => poweredUP.scan(), 3000)  → попытка переподключения

Clone this wiki locally