Java 通过 jlibmodbus 架包和PLC通讯
toqiye 2024-11-27 21:10 8 浏览 0 评论
最近项目上涉及到一些硬件设备,这些硬件设备是通过PLC来控制完成自动化的,而我们的项目是Java语言开发的,这就涉及到Java和PLC的通讯问题,之前没有接触过PLC,完全就不知道怎么去和PLC通讯,询问设备的厂家,厂家也只知道PLC的自动化编程,不知道怎么对外通讯,也咨询了一些搞PLC编程的人,他们也都是PLC之间的通讯,没做过和外部系统的通讯,没办法只能自己去查资料了解。
通过了解之后,发现PLC可以走开放式以太网的 Modbus Tcp 通讯,配置一个modbus tcp server服务,外部系统可以通过modbus tcp去和PLC进行通讯,取值或写值都可以,从而达到控制PLC的目的,下面说一下具体的配置。
PLC端的配置:
1、在PLC程序块中增加一个 modbus tcp server 服务
2、点击程序块,右键选择“库存储器”,建议从1000开始,防止地址冲突
3、设置你要控制的点,比喻说我要控制第1台电机和第3台电机启动,那我就在第1台电机设置一个 V101.0 的保持寄存器,在第3台电机设置一个 V105.0 的保持寄存器,并且都设置为常开状态
4、到此为止,PLC端配置完毕
Java端配置:
Java端的modbus-tcp调用,我们引用 jlibmodbus-1.2.9.7.jar 这个架包,通过这个架包去读取或设置PLC保持寄存器的值,从而达到控制PLC的目的
这里先对 V101.0 这个寄存器解释一下,V101.0 就是属于功能码03的V区寄存器,它是从下标0开始计算的,V0代表V区的第0个寄存器,V1代表V区的第1个寄存器,以此类推。而功能码03代表的V区是按照字来存放的,一个字有2个字节,一个字节有8位,一个寄存器有8位,所以一个字可以存放2个寄存器,所以V0和V1都是存放在V区第1个字里的,V0存放在高8位上,V1存放在低8位上。
寄存器都是按照二进制的8位来存放的,0代表开,1代表关。V101.0代表在V区第50个字里的第2个字节上存放的寄存器的第1位,0代表寄存器的第1位,7代表寄存器的第8位,寄存器的介绍到此为止,网上关于PLC的寄存器介绍都比较模糊,所以在此详细介绍一下。
jlibmodbus 功能码对应的方法:
下面上代码:
public class ModbusTcpUtil {
// 定义主机
private static ModbusMaster master = null;
// PLC地址
private static final String ip = "192.168.0.2";
// 默认端口,这里设置是默认端口502
private static final int port = Modbus.TCP_PORT;
// 从机地址
private static final int slaveId = 1;
static {
try {
// 设置主机TCP参数
TcpParameters tcpParameters = new TcpParameters();
// 设置TCP的ip地址
InetAddress adress = InetAddress.getByName(ip);
// TCP参数设置ip地址
tcpParameters.setHost(adress);
// TCP设置长连接
tcpParameters.setKeepAlive(true);
// TCP设置端口
tcpParameters.setPort(port);
// 创建一个主机
master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
// 设置自增的id
Modbus.setAutoIncrementTransactionId(true);
// 设置读取超时时间
master.setResponseTimeout(3000);
// 开启连接
master.connect();
} catch (Exception e) {
e.printStackTrace();
}
}
public static ModbusMaster getMaster() {
try {
// 主机是否在线
if (!master.isConnected()) {
// 开启连接
master.connect();
}
} catch (ModbusIOException e) {
e.printStackTrace();
}
return master;
}
/**
* @title: 获取保持寄存器的值
* @parem: offset V区字的地址
* @parem: quantity 字的数量
**/
public static void getSlaveV(int offset, int quantity) {
// 初始化
master = getMaster();
int[] registerValues = null;
try {
// 读取对应从机保持寄存器的数据
registerValues = master.readHoldingRegisters(slaveId, offset, quantity);
// 控制台输出
for (int value : registerValues) {
DecimalFormat decimalFormat = new DecimalFormat("0000,0000,0000,0000");
String value2 = decimalFormat.format(Long.valueOf(MutateUtil.toBin(value)));
System.out.println("===readHoldingRegisters===Address: " + offset++ + ", value: " + value + ", Value2: " + value2);
}
} catch (ModbusProtocolException e) {
e.printStackTrace();
} catch (ModbusNumberException e) {
e.printStackTrace();
} catch (ModbusIOException e) {
e.printStackTrace();
}
}
/**
* @title: 写入单个保持寄存器的值
* @parem: address V区字的地址
* @parem: value 字的值,值的内容是16位二进制转换的十进制
**/
public static void setSlaveV(int address, int value) {
// 初始化
master = getMaster();
try {
// 写入对应从机保持寄存器的数据
master.writeSingleRegister(slaveId, address, value);
} catch (ModbusProtocolException e) {
e.printStackTrace();
} catch (ModbusNumberException e) {
e.printStackTrace();
} catch (ModbusIOException e) {
e.printStackTrace();
}
}
// 二进制转换为十进制
public static int dectobin(String str) {
str = str.trim().replace(" ","");
int cnt = 0;
int sum = 0;
// 反转字符串
str = new StringBuilder(str).reverse().toString();
for (int i = 0; i < str.length(); i++) {
cnt++;
if (str.charAt(i) == '1') {
int mul = 1;
for (int j = 1; j < cnt; j++) {
mul *= 2;
}
sum += mul;
} else continue;
}
return sum;
}
// 十进制转换为二进制
public static String toBin(int num){
return conversion(num,1,1);
}
private static String conversion(int num, int diwei, int yiwei) {
// 如果num等于0,结果输出为0
if (num == 0){
return "0";
}
// 定义一个包含二进制、八进制、十六进制的表
char[] chs = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',};
// 定义一个临时容器
char[] arr = new char[32];
// 定义一个操作数组的指针
int pos = arr.length;
// 利用与低位最大值的方式取出低位,存到临时数组中
while(num != 0){
// --pos倒著往临时容器里存
arr[--pos] = chs[num & diwei];
// 无条件右移相应位数
num >>= yiwei;
}
StringBuffer stringBuffer = new StringBuffer();
// 转换后的结果
for( int x = pos; x < arr.length; x++) {
stringBuffer.append(arr[x]);
}
// 返回转换后的结果
return stringBuffer.toString();
}
public static void main(String[] args) {
// 设置 V101.0 的值,二进制0000 0000 0000 0001转换成十进制1,写入的值必须是十进制
setSlaveV(50, 1) ;
// 查询V区第50个字的值
getSlaveV(50, 1);
}
}
测试的结果:
到此为止,已经完成了Java和PLC之间的通讯,也完成了通过外部系统控制PLC的需求,特此记录一下,防止自己以后忘记了,便于追溯,也提供给有相同需求的程序员一个Demo,便于借鉴一二。
相关推荐
- 基于Python查找图像中最常见的颜色
-
如果我们能够得知道一幅图像中最多的颜色是什么的话,可以帮助我们解决很多实际问题。例如在农业领域中想确定水果的成熟度,我们可以通过检查水果的颜色是否落在特定范围内,来判断它们是否已经成熟。接下来我们将使...
- 出大要几次/圣彼得堡悖论
-
程序:fromrandomimportrandomdeffn():n=1whilerandom()<0.5:n+=1returnny=[fn()...
- 使用OpenCV测量图像中物体之间的距离
-
原文链接:https://www.pyimagesearch.com/2016/04/04/measuring-distance-between-objects-in-an-image-with-op...
- 让颜色更加饱满和有冲击力:图像颜色校正
-
大家拍照或图片时,获取会遇到图像颜色与实际颜色存在色差的现象。我们看一个标准色卡的图片:第一张图片就是有色差的图片,这种现象一般是相机或光线的原因造成的,我们可以通过标准色卡进行校正。第一张图片是有色...
- Python 数据分析 : 实例
-
1、构建矩阵生成4x4形式的矩阵,矩阵中的数据是1~10之间的随机数random_list=np.random.random(16)random_list=np.round(...
- 用这些免费开源的图标库,为你的项目画龙点睛
-
精致好看的图标能够为你的项目增色不少,今天我就整理了一期图标库精选系列,希望你可以从中找到自己喜欢的图标库。下面就跟我来一场视觉的盛宴,我会一一介绍GitHub上品牌、流行、极小,各具特色的免费精...
- ICON设计规范之图标尺寸
-
编辑导语:图标设计是UI设计中不可缺少的元素,它看似简单,但其实内含门道。本篇文章里,作者就对icon设计的相关知识和icon绘制方法做出经验介绍。如果你对icon设计也想要有所了解的话,那就点进来看...
- PHP开发必备VSCode插件(大全)
-
通用chinese(simplified...):简体中文语言包liveserverhtml:实时预览prettier-codeformatter:最流行的代码格式化插件...
- 增强用户体验:前端开发中HTML5和CSS3表格属性的应用与优化研究
-
摘要:本文探讨了在前端开发中HTML5和CSS3表格属性的应用与优化。首先介绍了HTML5中常用的表格元素和CSS3中丰富的表格样式属性,旨在帮助开发人员定制表格的外观和样式。其次,研究了表格结构的优...
- 产品经理小技术:图片素材随手找,原型设计快又好
-
数十万互联网从业者的共同关注!作者:牛冰峰博客:http://uxfeng.com/画图——这项古老而精细的做法,是一代代产品狗们得以传承的立足之本。草图、线框图、思维导图、PPT插图、数据汇报图表、...
- MAUI Blazor 项目实战 - 从0到1轻松构建多平台应用UI
-
前言最近在项目中尝鲜了MAUI,总体感受下来还是挺不错的,优缺点并存,但是瑕不掩瑜,目前随着.Net版本的迭代升级对它的支持也越来越友好,相信未来可期!感兴趣的朋友欢迎关注。文章中如有不妥的地方,也请...
- webstorm常用的插件
-
1、AtomMaterialIcons推荐原因:这款插件不仅...
- 「智能家居」自动化平台nodered第三方节点dashboard的使用
-
自带节点库讲完了,开始说说第三方节点库dashboard,该库提供另一个可配置的UI页面,可以配置一些表单元素,以及图表。先来看一下别人使用dashboard制作的面板吧,是不是很漂亮。接下来我们一...
- 「炫丽」从0开始做一个WPF+Blazor对话小程序
-
大家好,我是沙漠尽头的狼。...
- MAUI使用Masa blazor组件库
-
上一篇(点击阅读)我们实现了UI在Web端(BlazorServer/Wasm)和客户端(Windows/macOS/Android/iOS)共享,这篇我加上MasaBlazor组件库的引用,并...
你 发表评论:
欢迎- 一周热门
-
-
如何评估预测值与真实值之间的匹配质量
-
如何解决npm安装依赖报错ERESOLVE unable to resolve dependency tree
-
超详细的cmder工具介绍及功能、快捷键说明
-
畅网 N5105 四口 2.5G 小主机安装 WIN10 对比 WIN11 跑分
-
常见面试第三题之Activity的几种启动模式介绍
-
软件推荐丨gocron —— 定时任务管理系统
-
一分钟带你认识了解电信光猫(电信光猫有什么用途)
-
聊聊C++20最大的变革之一 —— Coroutine,看不懂你打我(一)
-
硬核!Rust异步编程方式重大升级:新版Tokio如何提升10倍性能详解
-
Vite 4.0 正式发布(vite版本)
-
- 最近发表
- 标签列表
-
- systemproperties (65)
- npm版本管理 (61)
- localhost:15672 (59)
- materialtheme (86)
- node-ssh (68)
- 图床搭建 (62)
- vue3addeventlistener (60)
- mybatisselectone (78)
- css圆形进度条 (69)
- androidble蓝牙开发 (62)
- android-gif-drawable (60)
- appender-ref (64)
- springbootmockito (68)
- css边框渐变色和圆角 (58)
- gsonfastjson (59)
- 依赖注入的方式 (62)
- cookie跨域共享 (63)
- easyexcel导出图片 (77)
- dp数组 (61)
- js获取兄弟节点 (68)
- sysctl-a (60)
- java知音 (58)
- window.target (62)
- apimodel注解的作用 (60)
- window.onerror (66)