在Java中通过BACnet协议读取和写入设备信息,主要依赖BACnet4J库实现。该库支持BACnet/IP通信,提供设备发现、属性读写、事件订阅等功能,适用于楼宇自动化系统开发。以下是具体实现步骤和代码示例:
一、环境准备
-
添加依赖
在Maven项目的
pom.xml中引入BACnet4J库(以6.0.1版本为例):<dependency> <groupId>com.infiniteautomation</groupId> <artifactId>bacnet4j</artifactId> <version>6.0.1</version> </dependency>同时需引入其他必要依赖(如日志库):
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.32</version> </dependency> -
模拟器准备
使用**Yabe(Yet Another BACnet Explorer)**模拟设备:
- 下载地址:https://sourceforge.net/projects/yetanotherbacnetexplorer/
- 安装后启动模拟器(
Bacnet.Room.Simulator.exe),创建虚拟设备并记录其Device ID。
二、核心代码实现
1. 初始化本地设备与网络
java
import com.serotonin.bacnet4j.LocalDevice;
import com.serotonin.bacnet4j.npdu.ip.IpNetwork;
import com.serotonin.bacnet4j.npdu.ip.IpNetworkBuilder;
import com.serotonin.bacnet4j.transport.DefaultTransport;
public class BACnetInitializer {
public static LocalDevice initializeLocalDevice(String localIp, int localPort) throws Exception {
IpNetwork ipNetwork = new IpNetworkBuilder()
.withLocalBindAddress(localIp) // 本机IP
.withSubnet("255.255.255.0", 24) // 子网掩码
.withPort(localPort) // BACnet默认端口47808
.withReuseAddress(true)
.build();
LocalDevice localDevice = new LocalDevice(1234, new DefaultTransport(ipNetwork)); // 本地设备ID
localDevice.initialize();
return localDevice;
}
}
2. 发现远程设备
`import com.serotonin.bacnet4j.RemoteDevice;
import java.util.List;
public class DeviceDiscovery {
public static List<RemoteDevice> discoverDevices(LocalDevice localDevice) {
localDevice.startRemoteDeviceDiscovery(); // 启动设备发现
try {
Thread.sleep(3000); // 等待发现完成(实际项目中建议用监听器)
} catch (InterruptedException e) {
e.printStackTrace();
}
return localDevice.getRemoteDevices(); // 获取设备列表
}
}`
3. 读取设备属性(仅监测)
java
import com.serotonin.bacnet4j.type.Encodable;
import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier;
import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier;
import com.serotonin.bacnet4j.util.RequestUtils;
public class PropertyReader {
public static Encodable readProperty(LocalDevice localDevice, RemoteDevice remoteDevice,
ObjectIdentifier objectId, PropertyIdentifier property) throws Exception {
return RequestUtils.getProperty(localDevice, remoteDevice, objectId, property);
}
// 示例:读取模拟输入(AI)对象的当前值
public static void readAnalogInput(LocalDevice localDevice, RemoteDevice remoteDevice, int instanceNumber) throws Exception {
ObjectIdentifier aiId = new ObjectIdentifier(ObjectIdentifier.ObjectType.analogInput, instanceNumber);
Encodable value = readProperty(localDevice, remoteDevice, aiId, PropertyIdentifier.presentValue);
System.out.println("AI值: " + value);
}
}
4. 写入设备属性(需控制权限)
java
import com.serotonin.bacnet4j.service.confirmed.WritePropertyRequest;
import com.serotonin.bacnet4j.service.confirmed.WritePropertyResponse;
import com.serotonin.bacnet4j.type.primitive.Primitive;
import com.serotonin.bacnet4j.type.primitive.Real;
public class PropertyWriter {
public static WritePropertyResponse writeProperty(LocalDevice localDevice, RemoteDevice remoteDevice,
ObjectIdentifier objectId, PropertyIdentifier property,
Primitive value) throws Exception {
WritePropertyRequest request = new WritePropertyRequest(objectId, property, null, value, null);
return (WritePropertyResponse) localDevice.send(remoteDevice, request);
}
// 示例:修改模拟输出(AO)对象的设定值
public static void writeAnalogOutput(LocalDevice localDevice, RemoteDevice remoteDevice, int instanceNumber, float newValue) throws Exception {
ObjectIdentifier aoId = new ObjectIdentifier(ObjectIdentifier.ObjectType.analogOutput, instanceNumber);
WritePropertyResponse response = writeProperty(localDevice, remoteDevice, aoId,
PropertyIdentifier.presentValue, new Real(newValue));
System.out.println("写入结果: " + (response.isPositiveAcknowledgement() ? "成功" : "失败"));
}
}
三、完整示例:读取温度并写入设定值
java
public class BACnetDemo {
public static void main(String[] args) {
try {
// 1. 初始化本地设备
LocalDevice localDevice = BACnetInitializer.initializeLocalDevice("192.168.1.100", 47808);
// 2. 发现远程设备
List<RemoteDevice> remoteDevices = DeviceDiscovery.discoverDevices(localDevice);
if (remoteDevices.isEmpty()) {
System.out.println("未发现设备!");
return;
}
RemoteDevice remoteDevice = remoteDevices.get(0); // 取第一个设备
// 3. 读取温度传感器(AI)值
PropertyReader.readAnalogInput(localDevice, remoteDevice, 0); // 假设实例号为0
// 4. 写入温控器(AO)设定值(需确保有写入权限)
PropertyWriter.writeAnalogOutput(localDevice, remoteDevice, 1, 25.0f); // 假设实例号为1,设定25℃
// 5. 终止本地设备
localDevice.terminate();
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、关键注意事项
- 跨网段通信 :BACnet/IP默认不支持跨网段,需配置**BBMD(BACnet Broadcast Management Device)**或使用VPN。
- 权限控制 :写入操作需设备支持且配置了正确的访问权限(如
Write Access属性)。 - 异常处理 :捕获
BACnetException处理网络超时、设备离线等问题。 - 性能优化 :批量读取属性时使用
ReadPropertyMultipleRequest减少网络开销。
五、替代方案
- Yabe工具:直接通过GUI查看/修改设备属性,适合快速测试。
- 其他库 :如
BACnet Stack for Java(开源实现),但功能较BACnet4J简陋。
通过上述方法,您可实现Java对BACnet设备的安全监测(只读) 和可控写入(需权限),满足楼宇自动化系统的多样化需求。