现有工具纳入版本管理: - ecat_tool.py: 一体化命令行主站(status/flash/eeprom/sdo/send) - ecat_server.py + ecat.html/css: 网页/桌面控制台(端口 8090) - ecat_scan.py / ecat_esc_regs.py / ecat_sniff.py: bring-up 扫描、 网络侧 ESC 寄存器诊断、裸帧抓包对照 - ecat_flash_eeprom.py: 经网线烧写 SII 镜像 + 逐字校验 - *.bat / *.vbs 启动入口 新增 .gitignore(排除 venv/ 与 __pycache__)、.gitattributes(不改写换行符)、 requirements.txt(锁定 pysoem 1.1.13 等依赖)、README.md。
79 lines
2.4 KiB
Python
79 lines
2.4 KiB
Python
"""_repro_hang.py — 复现 2026-09-03 01:40 "op/start 卡住→闪退"。
|
|
|
|
复刻 ecat.html 的 UI 操作序列(100ms 周期 + 2s pollSync + 手动 stop/start 节奏):
|
|
open → 循环 N 轮{ op/start → 立即 cycle+sync_diag → cycle@100ms → ~2s 后 op/stop } → close
|
|
faulthandler 45s 看门狗: 卡住时自动转储所有线程栈(op/start 有界 ~26s, 超过 45s = 真卡死)。
|
|
退出码非 0xC0000005 之外的异常退出也会打印。
|
|
"""
|
|
import faulthandler
|
|
import sys
|
|
import time
|
|
|
|
faulthandler.enable() # 捕获原生访问违例(SEH)并打印 Python 栈 = 闪退根因现场
|
|
faulthandler.dump_traceback_later(45, exit=False)
|
|
|
|
import ecat_server as es
|
|
|
|
|
|
def t():
|
|
return time.strftime("%H:%M:%S")
|
|
|
|
|
|
def show(res):
|
|
for line in res.get("log", []):
|
|
print(f" [{t()}] {line}", flush=True)
|
|
|
|
|
|
def main():
|
|
rounds = int(sys.argv[1]) if len(sys.argv) > 1 else 30
|
|
r = es.api_open({"substr": "I226"})
|
|
show(r)
|
|
if not r.get("ok"):
|
|
print("open 失败, 退出", flush=True)
|
|
return 1
|
|
|
|
for i in range(rounds):
|
|
print(f"=== round {i} op/start ===", flush=True)
|
|
r = es.api_op_start({"dc_cycle_us": 0})
|
|
show(r)
|
|
if not r.get("ok"):
|
|
print(f" [{t()}] op/start 失败, 继续下一轮", flush=True)
|
|
time.sleep(1.0)
|
|
continue
|
|
# 复刻 doCycle() 立即一次 + pollSync() 立即一次
|
|
r = es.api_op_cycle({"outputs": ""})
|
|
if not r.get("ok"):
|
|
show(r)
|
|
r = es.api_sync_diag({})
|
|
if not r.get("ok"):
|
|
show(r)
|
|
# 100ms 周期跑 ~2s, 中途 2s 处再 pollSync 一次
|
|
end = time.time() + 2.0
|
|
synced = False
|
|
while time.time() < end:
|
|
r = es.api_op_cycle({"outputs": ""})
|
|
if not r.get("ok"):
|
|
show(r)
|
|
break
|
|
if not synced and time.time() > end - 1.0:
|
|
synced = True
|
|
r = es.api_sync_diag({})
|
|
if not r.get("ok"):
|
|
show(r)
|
|
time.sleep(0.1)
|
|
print(f" [{t()}] op/stop", flush=True)
|
|
r = es.api_op_stop({})
|
|
show(r)
|
|
time.sleep(1.0) # 用户点击间隔
|
|
|
|
es.api_close({})
|
|
print(f"[{t()}] 全部 {rounds} 轮完成, 无卡死", flush=True)
|
|
faulthandler.cancel_dump_traceback_later()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
rc = main()
|
|
print(f"EXIT rc={rc}", flush=True)
|
|
sys.exit(rc)
|