【BACnet/IP协议-基于Bacnet4j读采集器点位数据 (实测)】

一、模拟BACnet/IP协议设备
  1. 下载Yabe模拟器
    链接:https://pan.baidu.com/s/1lvHhQYLPEYvPn8cjZIfLUg 提取码:xccl

  2. 默认下一步安装

  3. 安装后打开设备模拟器 Bacnet.Room.Simulator
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E8w1ZsWY-1655881246367)(C:\Users\coding\AppData\Roaming\Typora\typora-user-images\image-20220622105238010.png)]
    在这里插入图片描述

二、客户端查看模拟设备信息
  1. 打开Yabe客户端工具,按下图方式操作即可查看设备相关的属性和值等信息
    在这里插入图片描述
    在这里插入图片描述
  2. 或者使用BacnetScan客户端工具,查看设备的点位属性和值等信息
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJjdsreK-1655881246370)(C:\Users\coding\AppData\Roaming\Typora\typora-user-images\image-20220622144403244.png)]
    在这里插入图片描述
三、Bacnet4j包下载
四、代码读取点位
  1. 创建Maven项目,pom文件中引入Bacnet4j的jar

    <dependencies>
        <dependency>
            <groupId>com.infiniteautomation</groupId>
            <artifactId>bacnet4j</artifactId>
            <version>6.0.0</version>
        </dependency>
    </dependencies>
    
  2. Demo代码

    import com.serotonin.bacnet4j.LocalDevice;
    import com.serotonin.bacnet4j.RemoteDevice;
    import com.serotonin.bacnet4j.exception.BACnetException;
    import com.serotonin.bacnet4j.npdu.ip.IpNetwork;
    import com.serotonin.bacnet4j.npdu.ip.IpNetworkBuilder;
    import com.serotonin.bacnet4j.transport.DefaultTransport;
    import com.serotonin.bacnet4j.type.enumerated.ObjectType;
    import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier;
    import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier;
    import com.serotonin.bacnet4j.util.PropertyReferences;
    import com.serotonin.bacnet4j.util.PropertyValues;
    import com.serotonin.bacnet4j.util.ReadListener;
    import com.serotonin.bacnet4j.util.RequestUtils;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author HFL
     * @description 读取设备点位属性和值
     */
    public class ReadBacnetTest {
    
        static LocalDevice localDevice;
    
        private static final Integer REMOTE_DEVICE_ID = 295046;
    
        public static void main(String[] args) throws Exception {
            try {
                //创建网络对象
                IpNetwork ipNetwork = new IpNetworkBuilder()
                        //本机的ip
                        .withLocalBindAddress("172.18.42.29")
                        //掩码和长度,如果不知道本机的掩码和长度的话,可以使用代码的工具类IpNetworkUtils获取
                        .withSubnet("255.255.255.0", 24)
                        //默认的UDP端口
                        .withPort(47808)
                        .withReuseAddress(true)
                        .build();
                //创建虚拟的本地设备,deviceNumber随意
                localDevice = new LocalDevice(123, new DefaultTransport(ipNetwork));
                //初始化本地设备
                localDevice.initialize();
                //搜寻网段内远程设备
                localDevice.startRemoteDeviceDiscovery();
    
                //获取远程设备,instanceNumber是远程设备ID
                RemoteDevice remoteDevice = localDevice.getRemoteDeviceBlocking(REMOTE_DEVICE_ID);
    
                //获取远程设备的标识符对象
                List<ObjectIdentifier> objectList =  RequestUtils.getObjectList(localDevice, remoteDevice).getValues();
    
                List<ObjectIdentifier> aiList = new ArrayList<>();
                List<ObjectIdentifier> avList = new ArrayList<>();
    
                System.out.println("<===================对象标识符的对象类型,实例数(下标)===================>");
    
                //Object所有标识符
                for(ObjectIdentifier oi : objectList){
    
                    System.out.println(oi.getObjectType().toString() + "," + oi.getInstanceNumber());
    
                    /**
                     * ObjectIdentifier对象的不同类型的数据,进行过滤
                     * 此例子只列举了测试软件中存在的部分类型
                     * 具体业务时,根据需求,取对应类型的对象即可
                     * 对象类型(ObjectType):
                     *      analog-input、analog-value、binary-value、character-string-value、multi-state-value、...
                     * 对象属性标识符类型(PropertyIdentifier):
                     *      objectName、presentValue、description、eventState、units、...
                     */
    
                    //analog-input
                    if(oi.getObjectType().equals(ObjectType.analogInput)){
                        aiList.add(new ObjectIdentifier(ObjectType.analogInput, oi.getInstanceNumber()));
                    }
                    //analog-value
                    if(oi.getObjectType().equals(ObjectType.analogValue)){
                        avList.add(new ObjectIdentifier(ObjectType.analogValue, oi.getInstanceNumber()));
                    }
                    //....
                }
                System.out.println("<==================================================================>");
    
                System.out.println("取值开始!!!================>");
                //根据对象属性标识符的类型进行取值操作 [测试工具模拟的设备点位的属性有objectName、description、present-value等等]
                //analog-input
                PropertyValues pvAiObjectName = readValueByPropertyIdentifier(localDevice, remoteDevice, aiList, null, PropertyIdentifier.objectName);
                PropertyValues pvAiPresentValue = readValueByPropertyIdentifier(localDevice, remoteDevice, aiList, null, PropertyIdentifier.presentValue);
                PropertyValues pvAiDescription = readValueByPropertyIdentifier(localDevice, remoteDevice, aiList, null, PropertyIdentifier.description);
                for (ObjectIdentifier oi : aiList){
                    //取出点位对象不同类型分别对应的值
                    System.out.println(oi.getObjectType().toString() + " " + oi.getInstanceNumber() + " Name: " + pvAiObjectName.get(oi, PropertyIdentifier.objectName).toString());
                    System.out.println(oi.getObjectType().toString() + " " + oi.getInstanceNumber() + " PresentValue: " + pvAiPresentValue.get(oi, PropertyIdentifier.presentValue).toString());
                    System.out.println(oi.getObjectType().toString() + " " + oi.getInstanceNumber() + " Description: " + pvAiDescription.get(oi, PropertyIdentifier.description).toString());
                }
    
                //analog-value 同上,只是对象属性标识符列表不同List
                PropertyValues pvAvPresentValue = readValueByPropertyIdentifier(localDevice, remoteDevice, avList, null, PropertyIdentifier.presentValue);
                PropertyValues pvAvDescription = readValueByPropertyIdentifier(localDevice, remoteDevice, avList, null, PropertyIdentifier.description);
                for (ObjectIdentifier oi : avList){
                    System.out.println(oi.getObjectType().toString() + " " + oi.getInstanceNumber() + " PresentValue: " + pvAvPresentValue.get(oi, PropertyIdentifier.presentValue).toString());
                    System.out.println(oi.getObjectType().toString() + " " + oi.getInstanceNumber() + " Description: " + pvAvDescription.get(oi, PropertyIdentifier.description).toString());
                }
                System.out.println("================>取值结束!!!");
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if(null != localDevice){
                    //shutdown
                    localDevice.terminate();
                }
            }
        }
    
        /**
         * 读取远程设备的属性值 根据属性标识符
         * @param localDevice 本地设备
         * @param d 远程设备
         * @param ois 对象标识符列表
         * @param callback 回调
         * @param propertyIdentifier 属性标识符
         * @return 某一类属性的值
         * @throws BACnetException Exception
         */
        public static PropertyValues readValueByPropertyIdentifier(final LocalDevice localDevice, final RemoteDevice d,
                                                        final List<ObjectIdentifier> ois, final ReadListener callback, PropertyIdentifier propertyIdentifier) throws BACnetException {
            if (ois.size() == 0) {
                return new PropertyValues();
            }
    
            final PropertyReferences refs = new PropertyReferences();
            for (final ObjectIdentifier oid : ois) {
                refs.add(oid, propertyIdentifier);
            }
    
            return RequestUtils.readProperties(localDevice, d, refs, false, callback);
        }
    }
    
    
  3. 运行结果

    <===================对象标识符的对象类型,实例数(下标)===================>
    device,295046
    analog-input,0
    analog-input,1
    analog-input,2
    analog-value,0
    analog-value,1
    analog-value,2
    analog-value,3
    characterstring-value,1
    characterstring-value,2
    characterstring-value,3
    binary-value,0
    binary-value,1
    multi-state-value,0
    multi-state-value,1
    <==================================================================>
    取值开始!!!================>
    analog-input 0 Name: Temperature.Indoor
    analog-input 0 PresentValue: 20.6
    analog-input 0 Description: Indoor Temperature
    analog-input 1 Name: Temperature.Water
    analog-input 1 PresentValue: 34.0
    analog-input 1 Description: Glycol Water Temperature
    analog-input 2 Name: Temperature.Outdoor
    analog-input 2 PresentValue: 12.0
    analog-input 2 Description: Outdoor Temperature
    analog-value 0 PresentValue: 21.0
    analog-value 0 Description: Effective SetPoint
    analog-value 1 PresentValue: 21.0
    analog-value 1 Description: Setoint 1
    analog-value 2 PresentValue: 19.0
    analog-value 2 Description: Setpoint 2
    analog-value 3 PresentValue: 17.0
    analog-value 3 Description: Setpoint 3
    ================>取值结束!!!
    
五、常见问题
  1. 连接超时

    com.serotonin.bacnet4j.exception.BACnetTimeoutException
    	at com.serotonin.bacnet4j.transport.ServiceFutureImpl.result(ServiceFutureImpl.java:75)
    	at com.serotonin.bacnet4j.transport.ServiceFutureImpl.get(ServiceFutureImpl.java:64)
    	at com.serotonin.bacnet4j.util.RequestUtils.sendReadPropertyAllowNull(RequestUtils.java:175)
    	at com.serotonin.bacnet4j.util.RequestUtils.getObjectList(RequestUtils.java:162)
    	at com.serotonin.bacnet4j.util.RequestUtils.getObjectList(RequestUtils.java:156)
    	at ReadBacnetTest.main(ReadBacnetTest.java:52)
    Caused by: com.serotonin.bacnet4j.exception.BACnetTimeoutException
    	at com.serotonin.bacnet4j.transport.DefaultTransport.lambda$expire$23(DefaultTransport.java:957)
    	at com.serotonin.bacnet4j.transport.DefaultTransport$$Lambda$3/495523663.use(Unknown Source)
    	at com.serotonin.bacnet4j.transport.UnackedMessageContext.useConsumer(UnackedMessageContext.java:162)
    	at com.serotonin.bacnet4j.transport.DefaultTransport.expire(DefaultTransport.java:957)
    	at com.serotonin.bacnet4j.transport.DefaultTransport.run(DefaultTransport.java:525)
    	at java.lang.Thread.run(Thread.java:745)
    

    问题分析: UDP端口47808被占用,都是客户端连接工具(Yabe或BacnetScan)导致的
    解决: 关闭客户端连接工具,如果关闭后重启服务依旧连接超时, 可以查看47808端口是否被占用,如果被占用,将占用的进程kill掉即可

六、注意
1. LocalBindAddress必须是本机的IP,切记不能设置成Bacnet数据所在机器地址
2.只能进行跨主机通信,无法实现跨网段通信
  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值