1.BACnetIP下载编译
BACnetIP是基于BACnet协议开发,应用于EPICS系统的通讯协议。
BACnetIP:详细介绍BACnetIP如何使用。
BACnetIP代码下载:Github-BACnetIP
- BACnet v2.2.2 source: tar-archive-bacnetv2.2.2.tar.gz Date: 04/13/2022 Release Notes.txt
1.BACnet 下载之后,将目录下的configure/RELEASE下的EPICS_BASE指向正确路径
2.尝试使用makeBaseApp方法编译一个新的囊括BACnetIP的IOC,但是编译一直报错失败,于是尝试在安装包make之后的路径下,使用编译好的iocboot启动脚本。
2.配置st.cmd启动脚本
BACnet协议由于是局域网有效,无法跨网通讯,所以要保证IOC运行主机和BACnet协议设备处于同一网段,否则无法通讯
/* Define a BACnet Device... (<name>, <device-instance>, nic_name, <[port]>)*/
// i.p. addr for 192.168.20.43 "TC_BACnet"
drvBACnetDefineDevice("TC_BACnet", 1000, "ens224", 47808)
/*TC_BACnet代表连接IOC的名称,该名称要和使用的record中INP名称一样,如下
record(ai, "TC-CN:BUN:TCSRIWT")
{
field(DTYP, "BACnet")
field(INP, "@TC_BACnet 0 3 85")
field(SCAN, "1 second")
}*/
/*1000,指代独特的BACnet instance number,在BACnetScan可以看到,
BACnet/BACnetIP/Device:1000/(Analog Input,1)...(Analog Input 9),
分支Device:1000即为BACnet instance number*/
/* ens224,表示IOC设备的网口名称/网卡名称,即NIC_NAME,如ens224,ens33,eht0,
通过ifconfig命令查看使用该IP的网卡名称即可*/
/*47808,默认端口名称*/
BACnet instance number :1000
ens224:ifconfig命令获取IOC部署设备IP地址对应的网卡名称。
47808:BACnet标准端口,16进制为0xBAC0,十进制为47808。
运行cmd,如下代码为可运行的cmd
#!../../bin/linux-x86_64/bacnet
## You may have to change bacnet to something else
## everywhere it appears in this file.
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase "dbd/bacnet.dbd"
bacnet_registerRecordDeviceDriver pdbbase
# /** Define BACnet Stuff... */
# /** Define a BACnet Device... (<name>, <device-instance>, nic_name, <[port]>)*/
# i.p. addr for 192.168.21.38 "TC_BACnet"
drvBACnetDefineDevice("TC_BACnet", 1000, "ens224", 47808)
#drvBACnetDefineDevice("FieldServer", 11, "eth0")
drvBACnetSet("TC_BACnet", "apdu_timeout=3000, apdu_retries=3")
drvBACnetSet("TC_BACnet", "rpm_buffer_limit=500, rpm_disable=false")
## Load record instances
dbLoadRecords "db/Test_1.db"
## Set this to see messages from mySub
#var mySubDebug 1
## Run this to trace the stages of iocInit
#traceIocInit
cd ${TOP}/iocBoot/${IOC}
iocInit
drvBACnetStart
## Start any sequence programs
#seq sncExample, "user=8w4Host"
IOC运行结果如下
3.编译EPICS Records
3.1确定instance number
使用BACnet协议的设备会将他所有的属性封装在一起组成一个对象,而这些设备组成的对象团只能从对象自身的数据类型以及instance number来区分,标准数据类型是AI,AO,BI,BO.每个BACnet设备内部的对象(温度1、温度2、流量1....)都应具有独立的instance number,这个和BACnet instance number不同,互不影响,另外,同一数据类型下的instance number必须独一无二,但是不同数据类型的instance number可以相同。
比如BACnet instance number均为1000的连接下,每个对象独有对应的instance number
AI从上到下:1、10、2、3、4、5、6、7、8、9,均不同
AV:1不同类型可以相同
BI:0、1、2
3.2record编写
AI record
record(ai, "TC-CN:BUN:RHE-1-2-TOWT")
{
field(DTYP, "BACnet")
field(INP, "@TC_BACnet 0 1 85")
field(SCAN, "1 second")
}
record(ai, "TC-CN:BUN:PHE-1-2-TOWT")
{
field(DTYP, "BACnet")
field(INP, "@TC_BACnet 0 2 85")
field(SCAN, "1 second")
}
record(ai, "TC-CN:BUN:TCSRIWT")
{
field(DTYP, "BACnet")
field(INP, "@TC_BACnet 0 3 85")
field(SCAN, "1 second")
}
AO record
record(ao, "TC-CN:BUN:BunTSet")
{
field(DTYP, "BACnet")
field(OUT, "@TC_BACnet 2 1 85")
field(SCAN, "Passive")
}
BI/BOrecord
record(bi, "TC-CN:BUN:PUP-1-3WPS")
{
field(DTYP, "BACnet")
field(INP, "@TC_BACnet 3 1 85")
field(ZNAM, "OFF")
field(ONAM, "ON")
field(ZSV, "MAJOR")
field(OSV, "NO_ALARM")
field(SCAN, "1 second")
}
record(bi, "TC-CN:BUN:PUP-1-4WPS")
{
field(DTYP, "BACnet")
field(INP, "@TC_BACnet 3 2 85")
field(ZNAM, "OFF")
field(ONAM, "ON")
field(ZSV, "MAJOR")
field(OSV, "NO_ALARM")
field(SCAN, "1 second")
}
record(bo, "TC-CN:BUN:BunRun")
{
field(DTYP, "BACnet")
field(OUT, "@TC_BACnet 5 1 85")
field(ZNAM, "OFF")
field(ONAM, "ON")
field(ZSV, "MAJOR")
field(OSV, "NO_ALARM")
field(SCAN, "Passive")
}
3.3INP Format
INP字符串属性最少需要四个参数,也可以增加第五个作为数组下标
- @Device Name: 这个就是st.cmd文件内的名称,对应TC_BACnet.
- Object Type: 数据类型. Note: See enumerations below.
- Object Instance: 独立的instance number.
- Property Id: 获取对象属性值,当前值、对象名称、状态位等. Note: See enumerations below.
- [1..n] Array-Index: Used to denote the array-index if your property-Id is of array type
4.扩展之:驱动如何工作
运行st.cmd,BACnet连接成功,将对象打包,然后驱动程序遍历设备实例编号列表,然后向网络中广播,“谁“BACnet设备具有对应的实例编号。当处在该网络中的设备具有和广播的实例编号匹配的编号,就会向驱动回应“我是”这个实例编号的设备。
向驱动程序回应“我是”的设备,将回应包含远程设备的网络地址,可以将驱动程序绑定到远程设备。该响应还包含了远程设备的“最大APDU长度”,用于理解消息大小限制。
当驱动知道远程设备的网络地址后,驱动可以不需要广播的方式直接和设备对话。
接下来,驱动会向远程设备发送“读取属性RP”的服务请求,请求属于设备对象“协议服务支持”属性值,通过“Protocol_Services_Supported”结构体中的属性,驱动可以了解远程设备支持哪些BACnet服务协议,特别是“Read-Property-Multiple” (RPM) 服务。
接下来,驱动会向远程设备发送单独的 "Read-Property" (RP) 请求,请求内容就是在你的EPICS db文件内定义的属性内容。
一旦驱动完成了对单个设备属性定义的过程,然后,驱动程序会返回其内部的记录,了解设备是否支持 RPM 服务。请记住,这是驱动程序在设备上执行的第一次读取。
如果设备支持RPM服务,驱动程序就会尝试将读取的属性组合到一个单独的RPM请求中去,以优化其读取请求。
另外,如果设备不支持RPM服务,那么设备就会用RP服务继续一个一个的读取每一个属性值。
通过EPICS向BACnet设备写入属性值
BACnet 驱动程序中的所有写入都使用 ao-record 执行。因此,所有写入都是数字的。
优先级数组
在BACnet中,所有的对象都是可命令的,即所有的对象都是可写入的。有一个内置的优先级表,用于对客户端写入对象的值进行优先级排序,称为Priority-Array。
因此可命令对象的每一个写入值都必须具有与之一起写入的关联优先级。优先级由1到
,1对应最高优先级,16对应最低优先级。
优先级例子:假定有一个操作员用优先级为8 的操作让一个设备开启,这个属性在BACnet中对应
"Manual Operator",然而站点安全系统用优先级为1的命令同一设备关掉,这个属性对应"Manual-Life Safety",结果就是,设备仍然保持关闭或者关闭,因为优先级1等级更高。
发布优先级
在可命令对象的优先级组中每一个写入优先级的值都是有粘性的,意思是,除非客户端发出放弃的该优先级的命令,否则这个值会一直和这个优先级关联在一起。
为了放弃优先级和他的可命令值,客户端可以向优先级写入“NULL”值以表示客户端希望放弃该优先值。
回到上述“优先级例子“:一旦站点安全系统已经完成它的目标,然后站点安全系统向可命令对象的优先级数组中把优先级为1的值携程"null",让出”OFF“的命令,假定优先级8是下一个最高优先级,那么可命令对象新的优先级就到了优先级8。