BAC0配置为BACnet设备时的IPv4数据报异常与对象访问失败问题求助
BAC0配置为BACnet设备时的IPv4数据报异常与对象访问失败问题求助
我尝试用BAC0搭建一个模拟BACnet/IP设备,想让YABE工具能跨网络监控设备的数值,但运行代码时遇到了两个棘手的问题:一是触发了IPv4数据报相关的绑定错误,二是无法访问我定义的“Frequency”模拟输入对象。我已经查阅了BAC0的文档和库帮助文件,但还是没理清问题所在,希望能得到大家的帮助。
我的代码
#!/usr/bin/env python3 import asyncio from BAC0 import connect from BAC0.core.devices.local.factory import analog_input def defining_objects(device): from BAC0.core.devices.local.factory import ObjectFactory ObjectFactory.clear_objects() analog_input( instance=10, name="Frequency", properties={"units": "hertz"}, description="Frequency", presentValue=18.0, relinquish_default=21, ) return device async def main(): try: # Connect to BAC0 device #device1 = connect(ip="192.168.13.193/24", port="47808", deviceId="1110") device1 = connect(ip="192.168.13.193/24") defining_objects(device1) while True: device1["Frequency"].presentValue = 42.0 # Test value print("Device 1 Frequency:", device1["Frequency"].presentValue) await asyncio.sleep(2) except Exception as e: print(f"An error occurred: {e}") if __name__ == "__main__": asyncio.run(main())
运行时的错误输出
[12/16/24 12:00:06] INFO 2024-12-16 12:00:06,757 - INFO | notes.py:267 Starting Asynchronous BAC0 version 2024.09.10 (Lite) INFO 2024-12-16 12:00:06,760 - INFO | notes.py:267 Using bacpypes3 version 0.0.98 INFO 2024-12-16 12:00:06,761 - INFO | notes.py:267 Use BAC0.log_level to adjust verbosity of the app. INFO 2024-12-16 12:00:06,762 - INFO | notes.py:267 Ex. BAC0.log_level('silence') or BAC0.log_level('error') INFO 2024-12-16 12:00:06,786 - INFO | Lite.py:164 Using ip : 192.168.13.193/24 on port 47808 | broadcast : 192.168.13.255 [12/16/24 12:00:06] INFO 2024-12-16 12:00:06,789 - INFO | notes.py:267 Using default JSON configuration file INFO 2024-12-16 12:00:06,796 - INFO | notes.py:267 Registered as BACnet/IP App | mode normal INFO 2024-12-16 12:00:06,798 - INFO | notes.py:267 Device instance (id) : 3056500 An error occurred: 'Frequency' [12/16/24 12:00:06] INFO 2024-12-16 12:00:06,802 - INFO | notes.py:267 Installing recurring task Ping Task (id:128923930364320) Exception in callback IPv4DatagramServer.set_broadcast_transport_protocol(<IPv4Address 192.168.13.193>)(<Task cancell...nit__.py:150>>) handle: <Handle IPv4DatagramServer.set_broadcast_transport_protocol(<IPv4Address 192.168.13.193>)(<Task cancell...nit__.py:150>>)> Traceback (most recent call last): File "/home/eric/.local/lib/python3.10/site-packages/bacpypes3/ipv4/__init__.py", line 159, in retrying_create_datagram_endpoint return await loop.create_datagram_endpoint( File "/usr/lib/python3.10/asyncio/base_events.py", line 1385, in create_datagram_endpoint raise exceptions[0] File "/usr/lib/python3.10/asyncio/base_events.py", line 1369, in create_datagram_endpoint sock.bind(local_address) OSError: [Errno 99] Cannot assign requested address During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/eric/.local/lib/python3.10/site-packages/bacpypes3/ipv4/__init__.py", line 167, in retrying_create_datagram_endpoint await asyncio.sleep(BACPYPES_ENDPOINT_RETRY_INTERVAL) File "/usr/lib/python3.10/asyncio/tasks.py", line 605, in sleep return await future asyncio.exceptions.CancelledError During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3.10/asyncio/events.py", line 80, in _run self._context.run(self._callback, *self._args) File "/home/eric/.local/lib/python3.10/site-packages/bacpypes3/ipv4/__init__.py", line 207, in set_broadcast_transport_protocol transport, protocol = task.result() asyncio.exceptions.CancelledError Exception in callback IPv4DatagramServer.set_local_transport_protocol(<IPv4Address 192.168.13.193>)(<Task cancell...nit__.py:150>>) handle: <Handle IPv4DatagramServer.set_local_transport_protocol(<IPv4Address 192.168.13.193>)(<Task cancell...nit__.py:150>>)> Traceback (most recent call last): File "/home/eric/.local/lib/python3.10/site-packages/bacpypes3/ipv4/__init__.py", line 159, in retrying_create_datagram_endpoint return await loop.create_datagram_endpoint( File "/usr/lib/python3.10/asyncio/base_events.py", line 1402, in create_datagram_endpoint await waiter asyncio.exceptions.CancelledError During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3.10/asyncio/events.py", line 80, in _run self._context.run(self._callback, *self._args) File "/home/eric/.local/lib/python3.10/site-packages/bacpypes3/ipv4/__init__.py", line 176, in set_local_transport_protocol transport, protocol = task.result() asyncio.exceptions.CancelledError ------------------ (program exited with code: 0) Press return to continue
另外我还不确定能不能用WiFi网卡作为BACnet服务器,核心需求就是让YABE能监控到我代码里更新的数值,麻烦大家帮忙看看怎么配置才对。
问题分析与解决方案
1. 先解决对象访问失败的问题(An error occurred: 'Frequency')
这个问题是因为你直接调用analog_input()工厂函数创建对象,但没有把它注册到device1实例中。BAC0的ObjectFactory创建的对象不会自动关联到设备,需要用设备实例的API来添加。
修改defining_objects函数:
def defining_objects(device): # 去掉ObjectFactory.clear_objects(),避免清空意外对象 # 直接用device的add_analog_input方法创建对象,自动关联到设备 device.add_analog_input( instance=10, name="Frequency", units="hertz", description="Frequency", presentValue=18.0, relinquish_default=21, ) return device
这样创建的模拟输入对象会直接绑定到device1,之后用device1["Frequency"]就能正常访问了。
2. 解决IPv4数据报绑定错误(OSError: [Errno 99] Cannot assign requested address)
这个错误的核心是指定的IP地址无法被本地网卡绑定,常见原因和解决方法:
- 确认IP地址有效性:用
ip a(Linux)或ipconfig(Windows)检查本地WiFi网卡的实际IP,确保192.168.13.193是当前机器正在使用的有效IP - 自动绑定所有网卡:如果不确定具体IP,改用
connect(ip="0.0.0.0/24"),让BAC0自动绑定到所有可用的网络接口,这样兼容性更好 - 检查端口占用:BAC0默认用47808端口,用
ss -tulpn | grep 47808(Linux)或netstat -ano | findstr 47808(Windows)检查是否被其他程序占用,如果被占用可以在connect()时指定其他端口,比如connect(ip="0.0.0.0/24", port="47809") - 指定固定设备ID:为了方便YABE查找设备,建议在
connect()时指定固定的deviceId,比如connect(ip="0.0.0.0/24", deviceId="1110"),这样扫描时直接找设备ID 1110即可
3. 修改后的完整代码
#!/usr/bin/env python3 import asyncio from BAC0 import connect def defining_objects(device): device.add_analog_input( instance=10, name="Frequency", units="hertz", description="Frequency", presentValue=18.0, relinquish_default=21, ) return device async def main(): try: # 改用自动绑定所有网卡,指定固定设备ID方便YABE查找 device1 = connect(ip="0.0.0.0/24", deviceId="1110") defining_objects(device1) while True: device1["Frequency"].presentValue = 42.0 # Test value print("Device 1 Frequency:", device1["Frequency"].presentValue) await asyncio.sleep(2) except Exception as e: print(f"An error occurred: {e}") if __name__ == "__main__": asyncio.run(main())
4. 测试验证步骤
- 运行修改后的代码,确认控制台能正常输出
Device 1 Frequency: 42.0,没有对象访问错误 - 打开YABE工具,执行网络扫描(扫描范围选当前网段),找到设备ID为1110的BACnet设备
- 进入设备的“模拟输入”对象列表,找到实例10、名称为“Frequency”的对象,就能看到它的presentValue每2秒更新一次
- 如果还是看不到设备,检查防火墙是否放行47808端口,确保YABE和BAC0设备在同一网段,或者路由配置允许跨网段访问
关于WiFi网卡作为BACnet服务器的疑问
WiFi网卡完全可以作为BACnet服务器,只要它能正常接入网络,拥有有效IP地址,并且没有防火墙阻止BACnet的UDP 47808端口通信,就可以被YABE等工具扫描到。
备注:内容来源于stack exchange,提问作者Bangor Makerspace




