feat(A1+B1): 引入站台多主机识别、音频线缆分组互斥令牌、本地Modem仲裁、线缆分组管理WebUI
服务端 A1 改动(CloudServer/server.js): - stations 改为 nodes[] 多连接存储,同站台名的多台 CC 主机归入同一组(纯 stationName 精确分组) - 新增 getStationMaster():hasStationPeripheral + lastSeen 加权选 Master,音频只下发给 Master - 新增 audioCableGroups 数据结构(持久化到 server-data.json) - 新增 acquireCableToken/releaseCableToken:同线缆组全局互斥,高优先级发送 audio_preempt 抢占 - 新增 7 路由 REST API:/api/audio-cable-groups CRUD + 批量/单个站台成员管理 + PUT /stations/:name/cable-group 直接改组 - sendAudioControl 给同站组所有节点全量下发(stop/状态同步) - init_state / state_update 携带 audioCableGroups 给前端 - WS close 自动 removeStationWs 清理节点 + 释放令牌 客户端 B1 改动(station_broadcast.lua v1.0.99): - Peripherals 新增 wiredModems 扫描(排除无线),自动 rednet.open - 新增 LocalBus 命名空间: - station_hello 每 10s 广播 + 30s 超时清理邻居 - 同站台名自动组 stationGroup,按 hasStationPeripheral + nodeId 升序选 Master - 新增 Utils.isLocalStationMaster(),所有列车上报(arrival/approaching/alert/departure/schedule_sync)仅 Master 执行,彻底解决重复广播 - acquireLocalToken:audio_probe -> 80ms 收响应 -> 必要时 audio_preempt 抢占 -> token_grant - 令牌持有期间每 2s 广播 audio_playing 心跳,30s 超时自动判死回收 - Audio.playerLoop 在 playItem 前获取本地令牌,失败则跳过该音频 - Audio.stop() 自动释放本地令牌 - WS 新增下行处理:audio_preempt / cable_token_denied / station_group_state - station_identity.cfg 支持 audio_cable_group= 字段,saveStationIdentity 写回 - Network.register/heartbeat 携带 cableGroup 供服务端回填分组 Web 管理端(CloudServer/public/cablemanager.html,单文件无外部依赖): - 极简硬边风格,对齐 listmanager.html 配色和字体 - 分组网格卡片:色标/名称/描述/成员数/当前令牌持有者 + 编辑删除 - 新增/编辑 Modal:名称+描述+8色预设色板+自定义#RRGGBB - 分组详情双栏 Dual List:左栏全部站台(搜索+在线状态+在其他组标签)/ 右栏成员,中间批量添加移除按钮 - 顶部快捷操作:站台下拉+组下拉+确认,直接 PUT /stations/:name/cable-group - WebSocket 监听 state_update 实时刷新,Toast 提示反馈
This commit is contained in: