BACnet协议读取与发送

  • 注意
  • 我的提问:
  • 更新
  • 开发环境
  • BACnet相关基础知识
  • BACnet格式
  • BACnet代码
    • BACnet设备查找
    • BACnet设备读取
    • BACnet写入操作
    • AND其他...
    • **BACpypes库中没有提供非标准数据类型接口!!!!!**
  • 结束

因为项目的需求,需要对接某个厂商的BACnet协议。

可以说是这协议的坑真的不少,自学的时候遇到了一大堆问题…

注意

本文档仅对python完成读取与发送BACnet协议数据的流程做出教程,不对其中的BUG做出解释。一切以本人踩坑为准.jpg

我的提问:

我这边的需求:因为BACnet没有主动推送数据的方式,我现在是写了个循环,对device设备号进行轮巡读取。但是每轮光是读取就需要花费3秒时间(600+个device实例)。各位如果有什么方法优化这个轮巡的话可以直接写在评论区进行交流,先感谢各位了!

更新

因为BACnet的扫描与通信注定不能跨网段,如果要实现跨网段搜索,需要BBMD设备。

开发环境

依旧是python≥3.6,使用了BAC0库。直接PIP安装,这里放上相关的GitHub源码,调试的时候可能会用的上。
BAC0-GitHub
BACpypes(BAC0的依赖库)

BACnet相关基础知识

楼宇自动控制网络数据通讯协议(即: A Data Communication Protocol for Building Automation and Control Networks,简称《BACnet协议》)。其相关的基础知识,这里放上百度百科链接,感兴趣的可以去查询,我这边也只是做了初步的了解。
BACnet百度百科

BACnet格式

我这里直接使用了BACnet模拟器创建了一组虚拟的设备,设置了虚拟的参数以供自己调试。感谢网上大神们无私奉献的链接,直接拿来下载用了,这里也提供给大家。
BACnet模拟器:BACnet Simulator,验证码=gcfb
BACnet调试工具:BACnet调试工具,验证码=mjp7

其中,“Device X”称作device,子项目被称作type,子项目中的各项被称作属性和值,也就是property和value。在代码中,因为“-”具有很多特殊含义,于是代码中规定这些属性的名称按照小驼峰式命名规则。(例:present-value属性,在代码中的名称为presentValue)

BACnet代码

BACnet设备查找

BACnet协议允许通过whois方法扫描某一局域网下的BACnet设备。lite参数中的IP表示运行代码的本机IP,并非是BACnet设备或BACnet服务器的IP。

第一大坑:如果使用BACnet模拟器测试,那么BACnet模拟器和代码不能运行在同一电脑上,他们会互相占用对应端口!
顺便测试代码之前,把你的BACnet调试设备关掉,不然它也会占用端口。

import BAC0
myIPAddr = '192.168.1.1/24'
bacnet = BAC0.lite(ip=myIPAddr, )
bacnet.whois()
print(bacnet.whois())

打印输出:

2023-03-27 16:02:35,756 - INFO    | Starting BAC0 version 22.9.21 (Lite)
2023-03-27 16:02:35,756 - INFO    | Use BAC0.log_level to adjust verbosity of the app.
2023-03-27 16:02:35,756 - INFO    | Ex. BAC0.log_level('silence') or BAC0.log_level('error')
2023-03-27 16:02:35,756 - INFO    | Starting TaskManager
2023-03-27 16:02:35,757 - INFO    | Using ip : 192.169.1.1
2023-03-27 16:02:35,934 - INFO    | Starting app...
2023-03-27 16:02:35,935 - INFO    | BAC0 started
2023-03-27 16:02:35,935 - INFO    | Registered as Simple BACnet/IP App
2023-03-27 16:02:35,966 - INFO    | Update Local COV Task started
[('20:0x000000000000', 0), ('20:0x010000000000', 1), ('192.168.1.2', 4194302)]进程已结束,退出代码0

我这里用的是模拟器,我在模拟器中创建了两个device,显示出了两个模拟设备的网络地址和一个模拟器的地址。

BACnet设备读取

使用bacnet.read()方法。让我们先来读一读源码中的说明:

# 节选自C:\PycharmProjects\bacnet_test\venv\Lib\site-packages\BAC0\core\io\Read.pydef read(self,args,arr_index=None,vendor_id=0,bacoid=None,timeout=10,show_property_name=False,):"""Build a ReadProperty request, wait for the answer and return the value:param args: String with <addr> <type> <inst> <prop> [ <indx> ]:returns: data read from device (str representing data like 10 or True)*Example*::import BAC0myIPAddr = '192.168.1.10/24'bacnet = BAC0.connect(ip = myIPAddr)bacnet.read('2:5 analogInput 1 presentValue')Requests the controller at (Network 2, address 5) for the presentValue ofits analog input 1 (AI:1)."""

bacnet.read()函数接收一个类型为str的参数,字符串中的参数以空格为分隔,包含 addr type inst prop 四个参数.
之前我的模拟器输出了以下的device:
[('20:0x000000000000', 0), ('20:0x010000000000', 1), ('192.168.1.2', 4194302)]
在模拟器这里实际有用的只有前两个。我们以(‘20:0x010000000000’, 1) → AnalogInput 2 → presentValue作为测试项,那么

addr:20:0x010000000000
type:analogValue
inst:2
prop:presentValue

这就是最后的答案了。将其变成read函数需要的字符串参数,就是:'20:0x010000000000 analogValue 2 presentValue'
最终读取代码:

import BAC0
myIPAddr = '192.168.1.1/24'
bacnet = BAC0.lite(ip=myIPAddr, )
# bacnet.whois()  # 可以注释掉,这个的扫描时间比较长
# print(bacnet.whois())
data = bacnet.read('20:0x010000000000 analogValue 2 presentValue')
print(data)

完事了,你学会基本读取操作了

BACnet写入操作

同上,看看bacnet.write()源码说明:

    def write(self, args, vendor_id=0, timeout=10):"""Build a WriteProperty request, wait for an answer, and return status [True if ok, False if not].:param args: String with <addr> <type> <inst> <prop> <value> [ <indx> ] - [ <priority> ]:returns: return status [True if ok, False if not]*Example*::import BAC0bacnet = BAC0.lite()bacnet.write('2:5 analogValue 1 presentValue 100 - 8')Direct the controller at (Network 2, address 5) to write 100 to the presentValues ofits analogValue 1 (AV:1) at priority 8"""

和上面读取没啥大区别,多了一个‘-’和一个优先级。

bacnet.write('20:0x010000000000 analogValue 2 presentValue 12345.0 - 1')

就直接这么写入就可以了。

AND其他…

没错,测试环境很快就通过了,我就信心满满的实际测试了。然后就报错了

bacpypes.errors.InvalidTag: integer application tag required

…TAG标签错误???通过对代码的debug,找到了报错的触发代码:

def decode(self, tag):if (tag.tagClass != Tag.applicationTagClass) or (tag.tagNumber != Tag.realAppTag):raise InvalidTag("real application tag required")if len(tag.tagData) != 4:raise InvalidTag("invalid tag length")

就在(tag.tagNumber != Tag.realAppTag)这个判断上。debug告诉我,tag.tagNumber的值是3,Tag.realAppTag是源码中规定的值4。我到这里才去审查厂家给我提供的BACnet服务器的对接数据,结果发现他们的presentValue类型是Long,而不是Real。

这个时候,我坚定的认为自己在read和write函数里少填写了参数,或者用错函数了,导致数据类型不匹配(你想想,传的参数是个字符串啊,哪能规定数据类型),找到最后,除了在GitHub上找到了一个同样的问题但是没有回答,完全找不到任何的踪迹。于是这个时候我才死心塌地的去对着源码一步一步看。

BACpypes库中没有提供非标准数据类型接口!!!!!

我谢谢他啊…于是没办法,最后根据源码,直接修改了venv/lib/python3.6/site-packages/bacpypes/object.py源码中的数据类型:

@register_object_type
class AnalogValueObject(Object):objectType = 'analogValue'_object_supports_cov = Trueproperties = \[ ReadableProperty('presentValue', Integer)  # 原数据类型为Real, ReadableProperty('statusFlags', StatusFlags), ReadableProperty('eventState', EventState), OptionalProperty('reliability', Reliability), ReadableProperty('outOfService', Boolean), ReadableProperty('units', EngineeringUnits), OptionalProperty('minPresValue', Real), OptionalProperty('maxPresValue', Real), OptionalProperty('resolution', Real), OptionalProperty('priorityArray', PriorityArray), OptionalProperty('relinquishDefault', Real), OptionalProperty('covIncrement', Real), OptionalProperty('timeDelay', Unsigned), OptionalProperty('notificationClass',  Unsigned), OptionalProperty('highLimit', Real), OptionalProperty('lowLimit', Real), OptionalProperty('deadband', Real), OptionalProperty('limitEnable', LimitEnable), OptionalProperty('eventEnable', EventTransitionBits), OptionalProperty('ackedTransitions', EventTransitionBits), OptionalProperty('notifyType', NotifyType), OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3)), OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3)), OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3)), OptionalProperty('eventDetectionEnable', Boolean), OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference), OptionalProperty('eventAlgorithmInhibit', Boolean), OptionalProperty('timeDelayNormal', Unsigned), OptionalProperty('reliabilityEvaluationInhibit', Boolean), OptionalProperty('minPresValue', Real), OptionalProperty('maxPresValue', Real), OptionalProperty('resolution', Real), OptionalProperty('faultHighLimit', Real), OptionalProperty('faultLowLimit', Real), OptionalProperty('currentCommandPriority', OptionalUnsigned), OptionalProperty('valueSource', ValueSource), OptionalProperty('valueSourceArray', ArrayOf(ValueSource, 16)), OptionalProperty('lastCommandTime', TimeStamp), OptionalProperty('commandTimeArray', ArrayOf(TimeStamp, 16)), OptionalProperty('auditablePriorityFilter', OptionalPriorityFilter)]

就是我写了注释的那行,改完,跑起来了。

总结一下就是,我再也不想用python碰BACnet了,为什么都21.9.21版本了,BAC0库还是没有去尝试修改依赖库或是重写这部分的代码,传参竟然用的是字符串,以空格作为split的参数去分割参数…现在BACnet很多名称中都带有空格了,这库完全没有考虑到兼容性问题。

结束

国内基本都是各种复制粘贴的那三行代码,上面这么多很简单的问题,其实踩过一次坑就明白什么情况了,包括也没人对其中的参数做出一个说明,可惜没有人带我了解一遍这库。希望这篇文章能解决一部分人的问题吧。

BACnet协议读取与发送相关推荐

  1. java读取BACnet协议的设备信息-demo

    序 鉴于目前网上对于BACnet协议的相关文章比较少,所以写出这段时间对于java对接该协议的一个demo程序,供参考. 一.代码简介 本代码 gitee地址:https://gitee.com/Si ...

  2. BACnet协议介绍

    BACnet为BuildingAutomation andControlnetworks的简称,台湾通常翻译为"建筑自动化控制网路通讯协定",而中国大陆则译为"楼宇自动化 ...

  3. rtsp 协议读取视频进行分析并返回结果到websocket server

    一.rtsp 协议读取视频 1.1读取方法ffmpeg 这种方法和opencv是一样的,因为opencv使用的就是ffmpeg,结果不是很好,断线重连不是很好做,有一个好处是不用引入其他库,ffmpe ...

  4. BA-协议-BACnet 协议优势简析

    BACnet - Building Automation and Control Network 的简称,为楼宇自控网络制定 的网络和通讯协议 .由美国暖通空调工程师协会主导制定的开放的楼宇自控通讯标 ...

  5. spring boot 中使用 POP3协议读取并解析邮件

    spring boot 中使用 POP3协议读取并解析邮件 1.邮箱授权 QQ邮箱授权,打开 "设置" 切换到 "账户" 找到下图中设置,开启 "PO ...

  6. 认识BACnet协议

    一.什么是BACnet? BACnet,Building Automation and Control networks的简称,即楼宇自动化与控制网络.是用于智能建筑的通信协议. 一般楼宇自控设备从功 ...

  7. BACnet协议简要说明及组网简介

    主题 概要 BACnet协议 BACnet协议简要说明,组网简介 编辑 时间 新建 20160217 序号 参考资料 1 BACnet协议正文1995版 2 http://www.bacnet.org ...

  8. BACnet协议详解——应用层说明二

    文章目录 写在前面 3 BACnet APDU的传输 3.1 需确认的请求报文传输 3.2 分段的需确认请求报文的传输 3.3 分段的复杂确认报文的传输 3.4 分段确认APDU的传输 3.5 重复的 ...

  9. C#串口连接的读取和发送详解

    C#串口连接的读取和发送详解 一.串口连接的打开与关闭 串口,即COM口,在.NET中使用 SerialPort 类进行操作.串口开启与关闭,是涉及慢速硬件的IO操作,频繁打开或关闭会影响整体处理速度 ...

最新文章

  1. php 伪静态 page-18.html,PHP 伪静态实现技术原理讲解
  2. IT培训“来offer”获得数千万融资
  3. 【大牛系列教学】靠着这份面试题跟答案
  4. 防篡改对象之密封对象
  5. JQuery DataTables 列自己定义数据类型排序
  6. 学习笔记整理之小实现
  7. 【iOS开发】使用XCode 10添加Launch Image(启动图片)
  8. github创建远程仓库
  9. OpenGL超级宝典 绘制第一个三角形
  10. 你了解你的大脑吗?来看看脑科学如何诠释。
  11. React开发者工具 React Developer Tools 的下载
  12. 重置Windows 7网络设置
  13. 羊群效应?redis解决方案
  14. stm32使用各种传感器的教程
  15. jQuery之文档就绪事件
  16. 2022-2027年中国酒店及酒店管理市场竞争态势及行业投资前景预测报告
  17. PureMVC游戏框架解析 理解其中包含的设计模式
  18. Python打印空心等腰三角形
  19. icomoon矢量字体图标的使用
  20. NCC带审批流的主子表

热门文章

  1. http://cn.bing.com/ 微软的搜索引擎“bing 必应” 终于开通了……
  2. IOS pickerView 使用
  3. 锁相环(PLL)基本结构及相关基本知识
  4. 艺赛旗(RPA)python 的 tkinter 进度条的实现
  5. 软考A计划-软件设计师(高级程序员)考试大纲
  6. echarts 坐标自适应,实现 ECharts 图表自适应
  7. Python实现猫脸识别 | 喵主子福利
  8. Oracle应用之to_char(参数,'FM990.00')函数
  9. 利用Ajax访问后台数据
  10. 针对Intel芯片mac、win平台对cpu功耗、频率、电源等信息检测