Files
DCU_ECAT_Master/src/diag/ecat_scan.py
T
cbb 979da72181 refactor: 入口提到根目录, 代码收进 src/
上一版把入口塞进 launch/、代码摊在根目录, 正好反了: 双击入口该第一眼看到,
实现细节该收起来。现在:
  根目录  5 个 .bat/.vbs 入口 + README + requirements
  src/    ecat_server / ecat_tool / ecat_flash_eeprom / test_motor_backend
  src/web    前端资源      src/diag   独立诊断脚本

启动器改为 cd 进 %~dp0src 再跑(vbs 用 CurrentDirectory = base & "\src"),
工作目录落在 src 才能让 tool→flash_eeprom、test→server 的 import 解析到。
ecat_server.py 的 BASE_DIR 取自 __file__, 与 src/web 仍是同层父子关系,
_STATIC_MAP 和窗口模式的子进程 spawn 都无需改动。

.gitignore 补 sii.json / *.sii / eeprom_*.bin —— eeprom read --out 的导出
产物, 属运行结果不该入库。

验证: 7 个 py 全编译通过; test_motor_backend 8 项全通过; ecat.bat 与
run_scan.bat 实跑列出适配器; 服务起后 4 条静态路由 200 且 MIME 正确;
Edge headless 渲染主页与 motor 页, CSS 294 规则、9 节点样式生效, 零加载
失败零 JS 异常; bat 的 %~dp0 与 vbs 的 GetParentFolderName 实测均落在
项目根, src/ 下五个目标文件全部命中。
2026-09-03 22:27:23 +08:00

121 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
"""EtherCAT master bring-up test for the DCU LAN9252 slave (pysoem >= 1.1 API).
Usage:
ecat_scan.py list all Npcap adapters and exit
ecat_scan.py <name-substr> open the first adapter whose description matches
the substring (e.g. "I226"), scan the bus and
print slave identity + SII EEPROM dump
ecat_scan.py <substr> --op additionally request OP and run PDO cycles for 2s
"""
import sys
import time
try:
import pysoem
except ImportError:
print("pysoem 无法加载:请先安装 Npcap(本目录下的 npcap-1.88.exe,"
"安装时保持勾选 'Install WinPcap API-compatible Mode')")
sys.exit(1)
AL_STATE = {1: "INIT", 2: "PREOP", 4: "SAFEOP", 8: "OP"}
def al_state_name(code):
return AL_STATE.get(code, f"0x{code:02x}")
def list_adapters():
for i, a in enumerate(pysoem.find_adapters()):
desc = a.desc.decode() if isinstance(a.desc, bytes) else a.desc
print(f"[{i}] {desc}\n {a.name}")
def find_adapter(substr):
for a in pysoem.find_adapters():
desc = a.desc.decode() if isinstance(a.desc, bytes) else a.desc
if substr.lower() in desc.lower():
return a
return None
def dump_eeprom(slave, count=0x10):
print(" SII EEPROM dump (每行 8 个 word 地址,原始 32bit 返回):")
for base in range(0, count, 8):
words = []
for w in range(base, base + 8):
try:
v = bytes(slave.eeprom_read(w))
words.append(v.hex().upper())
except Exception:
words.append("ERRRRRRRR")
print(f" 0x{base:02X}: {' '.join(words)}")
def main():
if len(sys.argv) < 2 or sys.argv[1] in ("-l", "--list"):
list_adapters()
return
substr = sys.argv[1]
want_op = "--op" in sys.argv[2:]
adapter = find_adapter(substr)
if adapter is None:
print(f"没有找到描述包含 {substr!r} 的适配器,当前列表:")
list_adapters()
sys.exit(1)
desc = adapter.desc.decode() if isinstance(adapter.desc, bytes) else adapter.desc
master = pysoem.Master()
master.open(adapter.name)
try:
n = master.config_init()
except Exception as e:
print(f"总线上没有发现从站({type(e).__name__}: {e}):"
"请检查 DCU 板子是否上电、网线是否接到 I226-V 网口")
sys.exit(2)
if not n:
print("总线上没有发现从站:请检查 DCU 板子是否上电、网线是否接到 I226-V 网口")
sys.exit(2)
print(f"\n适配器: {desc}")
print(f"发现 {n} 个从站:")
for i, s in enumerate(master.slaves):
print(f" [{i}] name={s.name!r}")
print(f" VendorID=0x{s.man:08X} ProductCode=0x{s.id:08X} Revision={s.rev}")
print(f" AL state={al_state_name(s.state)} AL status code=0x{s.al_status:04X}"
f" DC={s.dc_sync}")
dump_eeprom(master.slaves[0])
if not want_op:
return
master.config_map()
if master.state_check(pysoem.SAFEOP_STATE, 2000) != pysoem.SAFEOP_STATE:
print(f"进入 SAFEOP 失败, 当前 AL 状态: {al_state_name(master.state)}")
sys.exit(3)
t_end = time.time() + 2.0
frames = 0
while time.time() < t_end:
master.send_processdata()
master.receive_processdata(2000)
frames += 1
time.sleep(0.001)
master.state = pysoem.OP_STATE
master.write_state()
if master.state_check(pysoem.OP_STATE, 2000) == pysoem.OP_STATE:
print(f"已进入 OP, PDO 循环 {frames} 帧正常")
else:
print(f"进入 OP 失败, 当前 AL 状态: {al_state_name(master.state)}")
if __name__ == "__main__":
main()