<Java物联网> 从主动到被动:Java中的BACnet设备属性查询

news2024/10/7 16:16:10

目录

BACnet

使用软件

资源

模拟器

使用Java主动查

 引入maven

创建网络对象

获取远程设备

获取设备属性

使用DeviceEventAdapter订阅

初始化本地BACnet设备和IP网络配置:

启动本地设备和添加监听器:

搜寻远程设备:

发送订阅COV报文:

修改值并等待:

SubscribeDevice监听器:


BACnet

BACnet(Building Automation and Control Network)是一种常用于楼宇自动化和控制系统的通信协议,它允许设备在楼宇管理系统中进行相互通信和控制。在Java中进行BACnet物联网操作,我们可以使用BACnet4J库,它是一个用于BACnet通信的Java库。

使用软件

资源

由于是个人学习,所以一般不会有真实硬件可以测试,所以我们还需要准备模拟器。

在这方面,我已经准备好了资源。

链接:https://pan.baidu.com/s/1Pd1cTpOkYZ9p4tbrAUihUg 
提取码:w62j

模拟器

拿其中一个模拟器Yabe来配合这次学习

安装好后,我们只需要用到这两个功能即可。

我们点击Simulator功能,会弹出这么一个模拟框。

这是模拟一个真实设备温度设备,底下的deviceId则为设备唯一标识。

我们同时打开多台,就可以发现他们的唯一标识不一样。

接下来打开Yabe

选择Add device,然后输入本机ip地址

然后你就会发现,它把我们刚刚打开的两台模拟设备扫描进去了。

选中其中一台设备后,下面Address Space会显示几行数据,对某个数据右键选择订阅后,你会发现,我们可以拿到模拟设备中的某个值的实时数据。

从而,我们能判断出,哪个属性值对应的是模拟设备中的什么,基于软件对设备的通讯到此就结束了。

重点在于我们如何使用Java来使用BACnet进行设备之间的通讯。

使用Java主动查

 引入maven

在开始示例之前,请确保已经下载并配置了BACnet4J库。

        <!-- https://github.com/infiniteautomation/BACnet4J -->
        <dependency>
            <groupId>com.infiniteautomation</groupId>
            <artifactId>bacnet4j</artifactId>
            <version>6.0.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/bacnet/bacnet4j-6.0.0.jar</systemPath>
        </dependency>
        

通过测试,单引用bacnet4j依赖,是远远不够的。使用起来会报各种异常,

比如

  • slf4j的 NoClassDefFoundError: org/slf4j/LoggerFactory
  • warp的 NoClassDefFoundError: lohbihler/warp/WarpScheduledExecutorService
  • commons的 NoClassDefFoundError: org/apache/commons/lang3/StringUtils

因此还需要引入以下maven依赖

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.32</version> 
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version> 
        </dependency>
        <dependency>
            <groupId>ai.serotonin.oss</groupId>
            <artifactId>sero-scheduler</artifactId>
            <version>1.1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/ai.serotonin.oss/sero-warp -->
        <dependency>
            <groupId>ai.serotonin.oss</groupId>
            <artifactId>sero-warp</artifactId>
            <version>1.0.0</version>
        </dependency>

创建网络对象

首先获取网段内的模拟设备,

//创建网络对象
IpNetwork ipNetwork = new IpNetworkBuilder()
    //本机的ip
    .withLocalBindAddress("192.168.1.12")
    //掩码和长度,如果不知道本机的掩码和长度的话,可以使用代码的工具类IpNetworkUtils获取
    .withSubnet("255.255.255.0", 24)
    //默认的UDP端口
    .withPort(47808)
    .withReuseAddress(true)
    .build();
//创建虚拟的本地设备,随意设置一个deviceId 设备的唯一标识
LocalDevice localDevice = new LocalDevice(LOCAL_DEVICE_ID, new DefaultTransport(ipNetwork));
//初始化本地设备
localDevice.initialize();
//搜寻网段内远程设备
localDevice.startRemoteDeviceDiscovery();

如果是ubuntu的情况下,建议去掉 withLocalBindAddress("192.168.1.12"),它会默认监听"0.0.0.0"这个地址,达到一样的效果。

跨网段的话,修改withSubnet()方法

获取远程设备

可以使用 LocalDevice 对象来获取远程设备,根据业务来选择以下方法:

//某个远程设备的id
private static final Integer REMOTE_DEVICE_ID = 311400;

//获取某个远程设备,REMOTE_DEVICE_ID是远程设备ID
RemoteDevice remoteDevice = localDevice.getRemoteDeviceBlocking(REMOTE_DEVICE_ID);

//获取所有远程设备
List<RemoteDevice> remoteDevices = localDevice.getRemoteDevices();

通过debug得知,获取到的数据没有区别。

获取设备属性

在物联网中,有一个叫物模型的抽象思想,其中有一个概念叫做属性,简单的说,一个温度检测设备里,它的在线离线是一个属性,温度也是一个属性。

接下来我们就去获取这个设备的所有属性。

//获取远程设备的标识符对象
List<ObjectIdentifier> objectList = RequestUtils.getObjectList(localDevice,remoteDevice).getValues();

通过debug可以看到这个属性集合中的数据 


是否觉得眼熟呢?是的,他就是对应上面进行订阅步骤的Address Space数据

 这里就是它的属性。

通过对比,我们可以知道Analog_Input:0是我们需要的温度属性。

 使用Java8获取温度属性对象

List<ObjectIdentifier> filter = objectList.stream().filter(e -> e.getObjectType().equals(ObjectType.analogInput) && e.getInstanceNumber() == 0).collect(Collectors.toList());

然后循环不断的获取该属性值的实时数据

while (true) {
                //根据对象属性标识符的类型进行取值操作 [测试工具模拟的设备点位的属性有objectName、description、present-value等等]
                //analog-input
                PropertyValues pvAiObjectName = readValueByProperty(localDevice, remoteDevice, filter, null, PropertyIdentifier.objectName);
                PropertyValues pvAiPresentValue = readValueByProperty(localDevice, remoteDevice, filter, null, PropertyIdentifier.presentValue);
                PropertyValues pvAiDescription = readValueByProperty(localDevice, remoteDevice, filter, null, PropertyIdentifier.description);
                for (ObjectIdentifier oi : filter) {
                    //取出点位对象不同类型分别对应的值
                    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());
                }
                Thread.sleep(1000);
}

抽一个通用的方法 readValueByProperty

public static PropertyValues readValueByProperty(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);
}

然后不断的读取和打印

 这样就可以不断获取最新的实时值。

使用DeviceEventAdapter订阅

  1. 初始化本地BACnet设备和IP网络配置:

    //创建网络对象
    IpNetwork ipNetwork = new IpNetworkBuilder()
        //本机的ip
        .withLocalBindAddress("192.168.1.12")
        //掩码和长度,如果不知道本机的掩码和长度的话,可以使用代码的工具类IpNetworkUtils获取
        .withSubnet("255.255.255.0", 24)
        //默认的UDP端口
        .withPort(47808)
        .withReuseAddress(true)
        .build();
    //创建虚拟的本地设备,随意设置一个deviceId 设备的唯一标识
    LocalDevice localDevice = new LocalDevice(LOCAL_DEVICE_ID, new DefaultTransport(ipNetwork));
    
     
    • 首先,它创建了一个本地BACnet设备,并指定设备号为123,并为其分配了一个默认的传输实例(DefaultTransport)。
    • 然后,它使用IpNetworkBuilder构建了一个IP网络配置,包括本地绑定地址、子网掩码和长度、默认UDP端口和地址重用等,
  2. 启动本地设备和添加监听器:

    • 接下来,它初始化本地设备,然后添加了一个SubscribeDevice的监听器,该监听器继承自DeviceEventAdapter
       
      //初始化本地设备
      localDevice.initialize();
      //添加监听器
      localDevice.getEventHandler().addListener(new SubscribeDevice());
  3. 搜寻远程设备:

    • 调用localDevice.startRemoteDeviceDiscovery()方法,搜寻网段内的远程BACnet设备。
       
      //搜寻网段内远程设备
      localDevice.startRemoteDeviceDiscovery();

  4. 发送订阅COV报文:

    • 接下来,它使用localDevice.send方法发送了一个订阅COV请求(SubscribeCOVRequest)到远程设备,以订阅特定对象的变化。
    • 订阅COV请求包括订阅标识、被监视对象的对象标识符、是否要发送确认报文以及订阅的时长。
       
    •  //发送订阅COV报文 对应为订阅标识(不可为0),订阅对象,是否要发送确认报文,订阅时长(0为永久)
      localDevice.send(remoteDevice, new SubscribeCOVRequest(new UnsignedInteger(1), new ObjectIdentifier(ObjectType.analogInput, 0), Boolean.TRUE, new UnsignedInteger(0))).get();
                  
  5. 修改值并等待:

    • 之后,进入一个无限循环,每隔2秒向远程设备的某个对象(ObjectType.analogInput, 0)写入值77。
    • 这会触发远程设备发送COV通知给本地设备。
       
    • while (true){
             //修改值为77
             RequestUtils.writePresentValue(localDevice, remoteDevice, new ObjectIdentifier(ObjectType.analogValue, 0), new Real(77));
             Thread.sleep(2000);
      }

  6. SubscribeDevice监听器:

    • SubscribeDevice是一个自定义的监听器类,继承自DeviceEventAdapter
    • 它实现了covNotificationReceived方法,该方法在收到COV通知时被调用,处理实时数据更新。
       
    • class SubscribeDevice extends DeviceEventAdapter {
          @Override
          public void covNotificationReceived(final UnsignedInteger subscriberProcessIdentifier,
                                              final ObjectIdentifier initiatingDevice, final ObjectIdentifier monitoredObjectIdentifier,
                                              final UnsignedInteger timeRemaining, final SequenceOf<PropertyValue> listOfValues)
          {
      
              if (listOfValues.get(0).getPropertyArrayIndex()!=null) {
                  System.out.println(listOfValues.get(0).getValue());
                  System.out.println("===========================================================");
              }
          }
      }

通过这种方法不断的获取最新值。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/782585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ansible自动化运维学习——综合练习

目录 (一)练习一 1.新建一个role——app 2.创建文件 3.删除之前安装的httpd服务和apache用户 4.准备tasks任务 (1)创建组group.yml (2)创建用户user.yml (3)安装程序yum.yml (4)修改模板httpd.conf.j2 (5)编写templ.yml (6)编写start.yml (7)编写copyfile.yml (8…

leetcode 46. 全排列

2023.7.23 目前位置回溯系列已经接触过 组合、分割、子集&#xff0c;本题是新类型&#xff1a;排列。排列的最大特点是&#xff1a;各集合有序&#xff0c;所以元素可以重复使用&#xff0c;因此不需要使用start了&#xff1b;需要使用一个used数组记录path中存在哪些元素了&a…

Unity-数据持久化-JSON

一、JsonUtlity JsonUtlity 是 Unity 自带的用于解析 Json 的公共类。它可以 将内存中对象序列化为 Json 格式的字符串 将 Json 字符串反序列化为类对象 1.在文件中存读字符串 using System.IO;// 1.存储字符串到指定路径文件中 // 第一个参数&#xff1a;填写的是 存储的…

四层和七层负载均衡详解

1 概述 四层负载均衡就是工作在计算机网络OSI七层分层的第四层&#xff08;传输层&#xff09;的&#xff0c;七层负载军和则是工作在第七层&#xff08;应用层&#xff09;的。即四层负载均衡是基于IP端口的负载均衡&#xff0c;七层负载均衡是基于URL等应用层信息的负载均衡…

Mysql数据库基本语句

1.数据库操作基本流程 ◼ 连接登陆数据库 ◼ 创建数据库 ◼ 创建数据表 ◼ 插入数据记录 ◼ 断开连接 连接 MySQL 数据库的命令语法格式&#xff1a; mysql [ -h 服务器 IP 或域名 -u 用户名 -p 密码 数据库名称 ]quit 或者 exit 退出数据库 注意事项&#xff1a; 操作指令…

力扣奇遇记 [第一章]

文章目录 &#x1f626;第一题&#xff1a;拿下LeetCode1769. 移动所有球到每个盒子所需的最小操作数学习内容&#xff1a;LeetCode1769. 移动所有球到每个盒子所需的最小操作数&#x1f648;思路分析&#xff1a;&#x1f496;代码产出&#xff1a; &#x1f626;第二题&#…

网络安全 Day16-计算机网络知识01

计算机网络知识01 1. 什么是网络2. 网络分类3. 网卡4. 网线5. MAC地址 1. 什么是网络 局域网&#xff08;私有网&#xff09;图片 路由器作用&#xff1a;隔离网络帮人选择最佳路径。内网地址就是网关。外网地址就是公网地址。 2. 网络分类 局域网&#xff1a;本地私有的一…

leetcode:1313. 解压缩编码列表(python3解法)

难度&#xff1a;简单 给你一个以行程长度编码压缩的整数列表 nums 。 考虑每对相邻的两个元素 [freq, val] [nums[2*i], nums[2*i1]] &#xff08;其中 i > 0 &#xff09;&#xff0c;每一对都表示解压后子列表中有 freq 个值为 val 的元素&#xff0c;你需要从左到右连接…

12.3.3 Shell script 的默认变量($0, $1...)

举例来说&#xff0c;如果你想要重新启动系统的网络&#xff0c;可以这样做&#xff1a; script 是怎么达成这个功能的呢&#xff1f;其实 script 针对参数已经有设置好一些变量名称了&#xff01;对应如下&#xff1a; 执行的脚本文件名为 $0 这个变量&#xff0c;第一个接的参…

AOP操作日志记录(将增、删、改相关接口的操作日志记录到数据库表中)

1.1 需求 需求&#xff1a;将案例中增、删、改相关接口的操作日志记录到数据库表中 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时&#xff0c;需要详细的操作日志&#xff0c;并保存在数据表中&#xff0c;便于后期数据追踪。 操作日志信息包含&#xff1a;…

mac电脑清理系统占用空间清理优化操作教程

mac系统广受用户的喜爱和信赖。但是&#xff0c;很多mac用户可能会发现&#xff0c;随着使用时间的增长&#xff0c;mac系统占用的空间越来越大&#xff0c;导致电脑运行缓慢&#xff0c;甚至出现磁盘空间不足的提示。那么&#xff0c;mac系统内存为什么那么大&#xff1f;mac清…

解析国产音频数模转换芯片的应用领域

数模转换器是用于数模转换的设备。数字信号定义为时间离散和幅度信号离散。同时,将模拟信号定义为时间连续和连续幅度信号。数模转换器将定点二进制数字(适当的抽象精度数字)转换为物理测量结果。数模转换器基于Nyquist-Shannon采样定理工作。它指出&#xff1a;如果采样率大于…

OJ练习第143题——二叉树展开为链表

二叉树展开为链表 力扣链接&#xff1a;114. 二叉树展开为链表 题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终…

多个deb包合并成一个deb包的方法

文章目录 前言一 deb基础知识1.1 目录结构1.2 包结构说明 二、多个deb包合并一个deb包2.1 下载离线deb包2.2 解压安装包2.3 deb包合并 三、使用合并deb包可能存在的问题四、总结 前言 在UOS系统中默认没有开启开发者模式&#xff0c;此时若需要安装多个deb包时&#xff0c;只能…

最短路算法(Dijkstra Bellman-Ford SPFA Floyd)

目录 最短路算法总览Dijkstra算法1.朴素Dijkstra算法算法步骤算法应用Dijkstra求最短路Ⅰ 2.堆优化Dijkstra算法算法步骤算法应用Dijkstra求最短路Ⅱ Bellman-Ford算法算法步骤算法应用有边数限制的最短路 SPFA算法算法步骤算法应用1. spfa求最短路2. spfa判断负环 Floyd算法算…

STM32MP157驱动开发——GPIO 和 和 Pinctrl 子系统的概念

文章目录 Pinctrl 子系统重要概念概述重要概念pin controller&#xff1a;client device&#xff1a; 代码中怎么引用 pinctrl GPIO 子系统重要概念概述在设备树中指定引脚在驱动代码中调用 GPIO 子系统头文件常用函数实例&#xff1a; BSP工程师针对芯片的寄存器写Pinctrl子系…

代码随想录day25

216. 组合总和 III 这道题和昨天的相似的地方&#xff0c;但是也有不同的特点。这道题规定了数字范围是从1-9&#xff0c;并且一个组合中&#xff0c;不能出现同样的元素&#xff0c;比如说[1&#xff0c;2&#xff0c;2]这个就不可以&#xff0c;如果取过2之后&#xff0c;就只…

在Visual C++中进行类设计的通行做法(下)——类的运行

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下在Visual C中进行类设计的通行做法&#xff0c;这一篇帖子来看看在搭建好基本架构并调整好重复定义问题后&#xff0c;怎么个运行法。程序员新手会去看很多书&#xff0c;但是书中往往…

安装centos7 ping不通宿主机

1、安装Virbox虚拟机 2、安装centos 7镜像 主要有两点配置需要注意&#xff1a; A:磁盘分区 B:网络设置&#xff0c;今天的实验主要是卡在网络配置这里 网络设置&#xff0c;使用的是仅主机模式且是手动分配静态IP,要点说明 看宿主机所用的网络是哪个&#xff0c;查看window的…

OC时钟/BC时钟介绍、Windows/Linux环境查看时钟频率

一、OC时钟和BC时钟介绍 OC时钟和BC时钟是指计算机体系结构中的两种不同的时钟信号。 OC时钟&#xff08;Off-chip clock&#xff09;是指在计算机系统的主板或外部设备上产生的时钟信号&#xff0c;它通过总线传输到CPU中&#xff0c;控制着CPU与主板或外部设备之间的数据传…