文章目录
- 基于Spring Boot开发上位机
- 背景
- 了解常用的PLC与通讯协议
- 引入相关的依赖
- 使用ModbusTCP进行数据读写
- 使用串口转Modbus进行数据读写(由于连接问题未测试成功)
- 使用Fins协议进行数据读写
- 处理PLC连接数量限制与频繁创建销毁连接问题
- 总结
基于Spring Boot开发上位机
背景
最近接到了自动化线的业务,客户方需要大屏展示各机台设备的数据,如温度、产量、速度、耗电量、开机时长、设备状态等情况,由于之前是使用**.NET+Winform** 实现的,上一任上位机开发工程师离职,所以之前的项目只做维护不做更改,本次属于使用Java对上位机进行重新开发。
了解常用的PLC与通讯协议
在进行上位机开发之前,我们需要先了解常见的PLC与相关的通讯协议
欧姆龙PLC: 欧姆龙PLC设备通过FINS协议进行通讯,由于FINS协议是欧姆龙PLC的内部协议,所以与其他PLC进行通信时还得给欧姆龙PLC安装相应的模块。
三菱PLC: 三菱PLC默认使用MELSEC协议,但在开发中常使用Modbus协议与三菱PLC通讯,需要对三菱PLC进行配置开通Modbus协议权限。
台达PLC: 台达PLC通常使用Modbus协议进行通讯,但是台达PLC内部的寄存器地址和Modbus寄存器地址并不相同,需要查看相应的操作手册进行操作。
汇川PLC: 汇川PLC通常使用串口进行通讯,在开发的过程中需要通过转换器把串口转网口,或者直接把串口转Modbus进行开发
西门子PLC: 西门子PLC通常使用S7协议进行通讯,S7协议又区分出了几种变体,主要为S7-1200/1500通讯协议与S7-300/400通讯协议,最常见的一般是S7-1200/1500通讯协议。
引入相关的依赖
本次上位机开发主要接触到前4种PLC,由于其中3种都可直接支持Modbus协议,所以主要引入的依赖为modbus协议相关的j2mod依赖与Fins协议相关的communication依赖,串口开发的相关依赖为jSerialComm,pom.xml加入以下内容。
<dependency>
<groupId>com.github.dathlin</groupId>
<artifactId>communication</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.ghgande</groupId>
<artifactId>j2mod</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.9.3</version>
</dependency>
如果communication依赖下载不了可直接下载本文提供的jar包进行导入
使用ModbusTCP进行数据读写
在j2mod中可直接使用ModbusTCPMaster进行创建连接,使用Register来对寄存器进行操作,读取的方法为readMultipleRegisters ,可读取多个连续的寄存器,写入的方法为writeMultipleRegisters 可写入多个连续的寄存器,代码示例如下:
//创建ModbusTCP连接,PLC的IP地址为192.168.20.60,ModbusTCP默认端口号为502
ModbusTCPMaster connection = new ModbusTCPMaster("192.168.20.60");
connection.connect();
if(connection.isConnected()) {
log.info("Connection Success");
}
//向寄存器中写入数据,使用Register数组对数据进行封装,一个Register代表一个寄存器
Register[] registers = new Register[4];
for (int i = 0; i < 4; i++) {
Register register = new ObservableRegister();
register.setValue(i);
registers[i] = register;
}
//从地址是5200的寄存器开始写入4个寄存器的数据
connection.writeMultipleRegisters(5200,registers);
//从寄存器读取数据,方法入参分别是开始的寄存器地址与读取的寄存器数量,从5200开始读取4个寄存器数据
InputRegister[] inputRegister = connection.readMultipleRegisters(5200,4);
for (InputRegister register : inputRegister) {
log.info("读取到的寄存器数据为:{} ",register.getValue());
}
//关闭连接
connection.disconnect();
使用串口转Modbus进行数据读写(由于连接问题未测试成功)
串口开发主要使用到的开发包为jSerialComm ,在j2mod中提供了ModbusSerialMaster来支持串口的连接,示例代码如下:
// 串口设置
SerialParameters serialParams = new SerialParameters();
//根据串口的名字设置串口连接
serialParams.setPortName("COM1");
// 创建Modbus串口连接
ModbusSerialMaster modbusSerialMaster = new ModbusSerialMaster(serialParams);
modbusSerialMaster.connect();
if (modbusSerialMaster.isConnected()) {
log.info("Connection Success");
}
//读取寄存器地址
InputRegister[] registers = modbusSerialMaster.readMultipleRegisters(5200, 2);
for (InputRegister register : registers) {
log.info("读取到的寄存器数据为{}", register.getValue());
}
//关闭连接
modbusSerialMaster.disconnect();
使用Fins协议进行数据读写
对于Fins协议的操作与ModbusTCP类似,原理都是建立连接—>操作寄存器,在Fins协议中可使用OmronFinsNet 建立连接,使用ReadUInt16 方法读取寄存器数据,使用Write 对寄存器进行数据写入,相关的代码示例如下:
//建立欧姆龙Fins连接,PLC的ip地址为192.168.20.90,欧姆龙PLC的默认端口为9600
OmronFinsNet omronFinsNet = new OmronFinsNet("192.168.20.90", 9600);
OperateResult result = omronFinsNet.ConnectServer();
if (result.IsSuccess) {
log.info("Connection Success");
}
//向D5200寄存器写入数据
omronFinsNet.Write("D5200",1);
//读取D5200寄存器的数据
log.info("D5200寄存器的数据为:{}",omronFinsNet.ReadUInt16("D5200").Content);
//关闭连接
omronFinsNet.ConnectClose();
处理PLC连接数量限制与频繁创建销毁连接问题
通常PLC的连接数量收到限制,但是在数据采集的过程中我们不可能频繁的去建立和关闭连接,此时可以采用本地缓存对连接进行持久化,当缓存失效时释放连接
使用缓存过期策略释放连接
/**
* 缓存过期的同时自动断开连接
*/
public static void setListener() {
cache.setListener((key1, cachedObject) -> {
if (cachedObject instanceof ModbusTCPMaster connection) {
if (connection.isConnected()) {
connection.disconnect();
}
}
if(cachedObject instanceof OmronFinsNet connection){
connection.ConnectClose();
}
});
}
建立连接的代码如下:
/**
* 获取modbus连接
* @param ipAddr plc地址
* @param port plc端口
* @return modbus连接
* @throws Exception 连接异常
*/
public static Object getModbusConnection(String ipAddr,Integer port) throws Exception {
if (LocalCacheUtil.containsKey(ipAddr)) {
return LocalCacheUtil.get(ipAddr);
} else {
//创建连接
ModbusTCPMaster connection = new ModbusTCPMaster(ipAddr,port);
connection.connect();
if(connection.isConnected()){
//注册缓存监听
LocalCacheUtil.setListener();
//设置connection在缓存中的过期时间
//由于使用lfu算法,缓存实际上并不会在到过期时间后立即进行释放,而是先淘汰最少使用的,然后每get一次都会进行续期
LocalCacheUtil.put(ipAddr, connection, 5000L);
return connection;
}
}
String error = MessageFormat.format("plc连接异常,ip:{0},port:{1}",ipAddr,port);
throw new RuntimeException(error);
}
总结
使用Java写上位机和C#写上位机的区别主要是开发包的问题,实际操作上都是建立连接—>操作寄存器