feat: 添加局域网设备发现功能和链路胶囊优化

- 实现局域网设备发现面板,支持探测ESP32中继板并自动切换端点
- 添加POST /api/lan/probe接口用于设备探测,返回三态判决结果
- 实现快扫和全段扫描两种模式,支持种子地址优先探测
- 添加端点唯一写手机制,防止多处同时修改设备IP
- 优化链路胶囊UI显示WiFi/EtherCAT/BLE连接状态和设备名称
- 修复EtherCAT链路OTA刷写期间PDO循环暂停和AL状态处理问题
- 调整PDO周期设置范围为0.125-4ms,支持亚毫秒显示为微秒
- 添加ESP32 WiFi使能开关控件样式和数据来源角标显示
This commit is contained in:
cbb
2026-10-13 00:48:34 +08:00
parent 514070b36d
commit eac3717f9a
41 changed files with 4229 additions and 934 deletions
+43 -1
View File
@@ -144,8 +144,50 @@ def verdict(ok, why=""):
return 1
def ask(prompt, timeout_s=600):
"""物理动作确认:必须等到人做完才继续,绝不自动放行。
终端里就是回车;被自动化/远程跑时 stdin 不是终端,改为等一个 ack 文件
(脚本先删掉它,出现即视为确认并再次删除)。两种路径都是人驱动的,只是
不靠 tty —— 换成"超时自动继续"就失去意义了,所以超时按 BLOCKED 收。
"""
import tempfile
# 显式指定了 ack 文件 = 明确要"文件握手",绝不先碰 stdin:后台任务的 stdin 未必
# 给 EOF,一旦落在 input() 上就是无限期吊着(实测踩过)。
ack = os.environ.get("DCU_GUARD_ACK_FILE")
if not ack:
if sys.stdin and hasattr(sys.stdin, "isatty") and sys.stdin.isatty():
try:
raw = input(prompt + "(回车继续 / 输入 s 放弃本测试): ").strip().lower()
if raw == "s":
raise Blocked("用户放弃")
return
except EOFError:
pass # isatty 为真但无输入 → 落到 ack 文件
ack = os.path.join(tempfile.gettempdir(), "dcu_guard_ack")
try:
os.remove(ack)
except FileNotFoundError:
pass
print(" ⏸ 需要人工:%s" % prompt)
print(" 做完后创建这个文件即可继续:%s" % ack)
print(" 等待上限 %ds,期间脚本不做任何总线动作" % timeout_s)
t0 = time.time()
while time.time() - t0 < timeout_s:
if os.path.exists(ack):
try:
os.remove(ack)
except OSError:
pass
print(" 收到确认(%.0fs)" % (time.time() - t0))
return
time.sleep(0.5)
raise Blocked("等待人工确认超时 %ds:%s" % (timeout_s, prompt))
__all__ = ["Link", "Blocked", "open_link", "guard_of", "preflight", "bridge_of",
"verdict", "GUARD_KEYS", "PAGE_BASE"]
"verdict", "ask", "GUARD_KEYS", "PAGE_BASE"]
if __name__ == "__main__":