我已经在 VS Code 和 PyCharm 中尝试过此操作,并且两者的行为方式相同。我有一个使用
pyads
的 python 应用程序,但我不确定 pyads
有什么特别有趣的地方。问题是,在任一 IDE 中,如果我在将作为 pyads
回调/通知执行的行上放置断点,代码会执行但不会中断。
对于
pyads
,我为特定 PLC 标签设置了通知。这会将回调函数与该标记关联起来,因此如果标记的值发生更改,我的回调就会执行。 pyads
只是 TcAdsDll.dll
的包装,看起来 pyads
使用 ctypes
来完成很多繁重的工作。当我在回调中放置断点并在 VS Code 或 PyCharm(两者都使用 pydevd)中运行代码时,代码会执行,但断点不会被命中。
此外,当我在应用程序中的不同位置打印进程 ID、线程 ID 和堆栈跟踪时,我可以看到进程 ID 相同,但在
pyads
回调中线程 ID 不同。堆栈跟踪特别有趣。在 pyads
回调之外,调用链如下所示:
runpy.py -> __main__.py -> cli.py -> pydevd_runpy.py -> my actual code
在回调内部,调用链看起来像这样
pyads\pyads_ex.py -> my callback
我对 pydevd 不太了解,但我知道所有
runpy.py -> __main__.py -> cli.py -> pydevd_runpy.py
的事情都是由 VS Code 完成的,用于设置调试器。我希望(尽管很容易出错)pydevd 也需要成为回调中调用链的一部分才能使我的断点起作用。
有谁对此有更多的见解,或者有什么我完全误解的吗?关于如何在这样的回调中设置断点有什么想法吗?谢谢!
我的 VS Code 启动配置非常基本:
{
"name": "run-my-thing",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/run-my-thing.py",
"console": "integratedTerminal",
"justMyCode": true
}
你说得对:
pyads
用ctypes
模块包装你的回调,它从C DLL中调用,我猜Python调试器不会拾取它。
我设法用两种不同的方法打破了回调:
breakpoint()
:通过将其添加为回调中的代码,会弹出带有 pdb
的控制台,可以在其中进行控制台调试。然而,这似乎不能与 PyCharm 一起使用。pydevd.settrace()
:使用 PyCharm 调试器可以实现。
一个完整的例子:
from pyads import Connection
import pyads
from time import sleep
import threading
import multiprocessing
import traceback
import sys
try:
import pydevd
except ModuleNotFoundError:
pydevd = None
def print_info():
print("Trace:", threading.gettrace())
print("Thread ID:", threading.get_ident())
print("PID:", multiprocessing.current_process().pid)
print("Callstack:")
traceback.print_stack(file=sys.stdout)
print()
def main():
plc = Connection(
ams_net_id="169.254.83.50.1.1",
ams_net_port=851,
ip_address="192.168.56.10",
)
print_info()
@plc.notification(pyads.PLCTYPE_UDINT)
def my_callback(handle, name, timestamp, value):
if pydevd:
pydevd.settrace()
print("New value:", value)
print_info()
with plc:
symbol = plc.get_symbol("MAIN.counter")
symbol.add_device_notification(my_callback)
sleep(10)
return
if __name__ == "__main__":
main()