分别使用netty和apache.plc4x测试读取modbus协议的设备信号

记录一下常见的工业协议数据读取方法

目录

  • 前言
  • Modbus协议说明
  • Netty 读取测试
  • 使用plc4x 读取测试
  • 结束语

前言

Modbus 是一种通讯协议,用于在工业控制系统中进行数据通信和控制。Modbus 协议主要分为两种常用的变体:Modbus RTU 和 Modbus TCP/IP

  • Modbus RTU:Modbus RTU 是一种基于串行通信的协议。

  • Modbus TCP/IP:Modbus TCP/IP 是一种基于 TCP/IP 网络的协议。
    本次使用TCP协议,一般常见使用这种协议。

Modbus 协议一般工业设备例如光电信号,各类传感器和执行器等。
一些电力设备(如变压器、开关设备、仪表等)

Modbus协议说明

如果要使用netty读取modbus协议数据必须了解一下协议报文格式。

参考: https://neugates.io/docs/zh/latest/appendix/protocol/modbus_tcp.html

如果设备数量< 30个可以尝试使用 Neuro 产品读取,里面包含配置监控SDK等。

Modbus=MBAP(报文头)+PDU(帧结构)

Netty 读取测试

假设有一个光电IO模块对接了8个激光设备,激光扫描到障碍物为1 没有扫到位0,先通过厂家自带的web端管理界面查看目前的实际信号情况:
在这里插入图片描述

这边测试的Netty代码如下:


package org.example.modbus;import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;public class ModbusClient {private final String host;private final int port;public ModbusClient(String host, int port) {this.host = host;this.port = port;}public void start() throws InterruptedException {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel ch) {ch.pipeline().addLast(new ModbusClientHandler());}});ChannelFuture f = b.connect(host, port).sync();f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}private static class ModbusClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) {// writeShort 一次写2个字节   writeByte 一次写1个字节ByteBuf buffer = Unpooled.buffer();// >>>>>>>>>>>>构造 M B A P Header(报文头)<<<<<<<<<<<<<// 事务标识符 占用2个字节// 可以解释为报文的序列号,例如测试使用的 Modbus Poll 客户端一直发送数据,// 所以每发送一次数据标识符就加一。服务器接收时会把这个数据原封返回。buffer.writeShort(1);// 协议类型 占用2个字节, 十六进制格式"00 00" 表示Modbus TCP 协议buffer.writeShort(0);// 长度 占用2个字节, 6 表示报文长度(后面有6个字节),包括 M B A P Header 和 PDU// 表示从单元标识符开始后面数据的长度。如:00 06 表示后面有 0X06 个字节长度的数据。buffer.writeShort(6);// 单元标识符 占用1个字节, 17 表示设备存储单元编号buffer.writeByte(17);// >>>>>>>>>>>>构造 PDU PDU=功能码+数据<<<<<<<<<<<<<// 功能码 占用1个字节, 02 表示读离散量输入buffer.writeByte(2);// 开始读的数据的地址。从 00 32 开始读数据。buffer.writeShort(32);// 读取的寄存器数量。从开始位置读 00 08 个寄存器数据。buffer.writeShort(8);ctx.writeAndFlush(buffer);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf buffer = (ByteBuf) msg;byte[] response = new byte[buffer.readableBytes()];buffer.readBytes(response);System.out.println("Response: " + bytesToHex(response));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}}private static String bytesToHex(byte[] bytes) {// 这个 bytesToHex 方法用于将一个 byte 数组转换为十六进制格式的字符串。// 每个字节被转换为两个十六进制字符,并用空格分隔,// 最终返回一个表示十六进制表示形式的字符串StringBuilder sb = new StringBuilder();for (byte b : bytes) {sb.append(String.format("%02X ", b));}return sb.toString();}public static void main(String[] args) throws InterruptedException {String host = "192.168.50.41";int port = 28899;new ModbusClient(host, port).start();}
}

光电IO设备模块IP地址为 192.168.50.41 端口使用 28899,上面代码是使用netty 向IO模块发送读取 8个光电的离散量信号报文,地址从32开始,然后获得modbus协议的结果报文,最终获得的结果报文解析成十六进制的字符串形式如下:

Response: 00 01 00 00 00 04 11 02 01 CD 

还是建议参考上面推荐的文档,这里截取主要信息:
在这里插入图片描述
根据上面的样例说明,我们其实想要得到的结果是最后2位16进制数据 DD,占据1个字节,因为我们读取的是离散值(类似true或fase 一般是1或者0),因此我们将 CD转换为二进制数据:

1 1 0 1 1 1 0 1

从低位开始(从右 至 左) 对应厂家web管理界面中的 DI-1 DI-2 …
绿色=1 灰色=0
在这里插入图片描述
可以发现netty读取到的信号和厂家web管理界面显示的数据一致。

使用plc4x 读取测试

apache旗下工业协议适配工具库,具体文档查看官网:
链接: https://plc4x.apache.org/users/protocols/modbus.html

pom.xml文件引入maven依赖包:

    <properties><plc4x.version>0.12.0</plc4x.version></properties><dependency><groupId>org.apache.plc4x</groupId><artifactId>plc4j-api</artifactId><version>${plc4x.version}</version></dependency><dependency><groupId>org.apache.plc4x</groupId><artifactId>plc4j-driver-modbus</artifactId><version>${plc4x.version}</version></dependency>

代码如下:

package cn.guzt.modbustest;import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.PlcDriverManager;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.apache.plc4x.java.api.messages.PlcReadResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** apache PLX modbus测试用例** @author guzt*/
public class ModbusExample {protected static final Logger logger = LoggerFactory.getLogger(ModbusExample.class);public static void main(String[] args) {String ip = "192.168.50.41";String port = "28899";// 单元标识符:相当于设备的地址String unitIdentifier = "17";String timeout = "5000";String urlFormat = "modbus-tcp:tcp://{}:{}?unit-identifier={}&request-timeout={}";// try里面会自动关闭连接try (PlcConnection plcConnection = PlcDriverManager.getDefault().getConnectionManager().getConnection(StrUtil.format(urlFormat, ip, port, unitIdentifier, timeout))) {// Check if this connection support reading of data.if (!plcConnection.getMetadata().isReadSupported()) {logger.info(">>>>>>>>>>>>>>This connection doesn't support reading.");return;}// Check if this connection support writing of data.if (!plcConnection.getMetadata().isWriteSupported()) {logger.info(">>>>>>>>>>>>>>This connection doesn't support writing.");return;}if (plcConnection.isConnected()) {logger.info(">>>>>>>>>>>>>>Modbus已经连上..............");}// Create a new read request:// You will need to pass the reference you are asking forPlcReadRequest.Builder builder = plcConnection.readRequestBuilder();// 一次性读取几个寄存器里面的内容int count = 8;// 这里面的起始地址为实际为 32,传递参数时候加1int startAddress = 33;for (int i = 0; i < count; i++) {// 功能码 (tagAddress) Modbus 的操作对象有四种:线圈、离散输入、输入寄存器、保持寄存器。// 1. 线圈:相当于开关,在 Modbus 中可读可写,数据只有 00 和 01。// 2. 离散量:输入位,开关量,在 Modbus 中只读。// 3. 输入寄存器:只能从模拟量输入端改变的寄存器,在 Modbus 中只读。// 4. 保持寄存器:用于输出模拟量信号的寄存器,在 Modbus 中可读可写。// 查看参考:https://neugates.io/docs/zh/latest/appendix/protocol/modbus_tcp.html// 不同功能码对应不同的地址格式:参看 org.apache.plc4x.java.modbus.base.tag.ModbusTagHandlerbuilder.addTagAddress("第" + (i + 1) + "个光电信号:", "discrete-input:" + (startAddress + i));}// 这种方式一次性读取8个:builder.addTagAddress("DI-count8N", "discrete-input:33:BOOL[8]")PlcReadRequest readRequest = builder.build();logger.info(">>>>>>>>>>>>>>开始读取");// Execute the requestPlcReadResponse response = readRequest.execute().get();// Handle the response// 创建了一个写请求,尝试将地址1的线圈设置为truefor (String fieldName : response.getTagNames()) {if (response.getObject(fieldName) instanceof Boolean) {logger.info(">>>>>>>>>>>>>>Boolean[" + fieldName + "]: " + response.getBoolean(fieldName));} else if (ArrayUtil.isArray(response.getObject(fieldName))) {logger.info(">>>>>>>>>>>>>>Array[" + fieldName + "]: " + response.getObject(fieldName));} else {logger.info(">>>>>>>>>>>>>>Object[" + fieldName + "]: " + response.getObject(fieldName));}}} catch (Exception e) {e.printStackTrace();}}
}

执行打印结果如下:

...
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第1个光电信号:]: true
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第2个光电信号:]: false
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第3个光电信号:]: true
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第4个光电信号:]: true
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第5个光电信号:]: true
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第6个光电信号:]: false
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第7个光电信号:]: true
16:20:25.378 [main] INFO cn.guzt.modbustest.ModbusExample - >>>>>>>>>>>>>>Boolean[第8个光电信号:]: true...

从第1行至第8行记录值 对应厂家web管理界面中的 DI-1 DI-2 …
绿色=true 灰色=fase
在这里插入图片描述
可以发现plc4x读取到的信号和厂家web管理界面显示的数据一致。

结束语

读取Modbus的开源库有很多,这里列举常见的使用库,尤其是 Plc4x 这个适配了主流的工业协议,值得我们去研究。

使用netty的话对基本功要求比较高,如果对modbus工业协议包括TCP/IP协议一知半解估计应该是写不出成功案例。

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

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

相关文章

通过验证邮箱进行注册信息确认

应用在进行注册时&#xff0c;避免恶意攻击和垃圾注册&#xff0c;可以通过验证注册者身份后才能够提交。一般可以使用验证手机短信或者验证邮箱&#xff0c;验证短信会有专门的第三方服务&#xff0c;可以进行付费购买。验证邮箱的正确与否&#xff0c;可以通过以下2种方式进行…

Linux 安装 MySQL 8.0.26

1、MySQL 8.0.26 下载 官方网站下载 MySQL 8.0.26 安装包&#xff0c;下载地址&#xff1a;mysql8.0.26 本案例采用Linux 64位操作系统进行讲解&#xff0c;通过wget命令下载安装包。 使用df -lh命令查看&#xff0c;磁盘大小&#xff0c;尽量安装在比较大的磁盘下&#xff0c…

vue3+ts <script setup lang=“ts“> element-plus的el-date-picker设置默认日期

效果图&#xff08;单个日期&#xff09;&#xff1a; utils.ts&#xff1a; /*** 格式化时间戳* param {number} timestamp 时间戳* param {string} format 格式* returns {string}*/ export const formatTimeStamp (timestamp: number, format: string) > {if (!timesta…

Linux系统防火墙iptables(下)

备份与还原iptables规则设置 1、yum -y install iptables iptables-services 安装iptables软件包 2、systemctl start iptables.service 开启服务 3、systemctl enable iptables.service 开机自启 我们对iptables命令行中的设置&#xff0c;都是临时设置&#xff0c;只要遇到服…

GPT-5

欢迎来到 Papicatch的博客 文章目录 &#x1f349;技术突破预测 &#x1f348;算法进步 &#x1f348;理解力提升 &#x1f348;行业推动力 &#x1f349;人机协作的未来 &#x1f348;辅助决策 &#x1f348;增强创造力 &#x1f348;复杂任务中的角色 &#x1f348;人…

深入剖析Tomcat(十三) Host、Engine 容器

前面很多篇文章都在介绍Context与Wrapper两个容器&#xff0c;因为这两个容器确实也比较重要&#xff0c;与我们日常开发也息息相关&#xff0c;但是Catalina是存在四个容器的&#xff0c;这一章就来简单看看Host与Engine这两个容器。 再次展示下Catalina的容器结构&#xff0…

VS2022(Visual Studio 2022)最新安装教程

1、下载 1、下载地址 - 官网地址&#xff1a;下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux - 根据自己的电脑的 【操作系统】 灵活选择。 2、安装包 【此处为Windows系统安装包】 2、安装 1、打开软件 - 右击【以管理员身份打开】&#xff0c; 2、准备配置 …

Web Serial串口通信实现WEB浏览器读写M1卡

本示例使用的设备&#xff1a;RS232串口RFID NFC IC卡读写器可二次开发编程发卡器USB转COM-淘宝网 (taobao.com) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> &l…

优思学院|工厂的部门架构管理与精益生产

工厂内有不同部门&#xff0c;各部门之间必须协调合作才能发挥整体功能。工厂最主要的部分是制造产品的现场&#xff0c;这里安装了生产工具&#xff0c;还有操作员进行加工或生产制造。 制造时使用的材料或零组件&#xff0c;需要对外采购。对于加工组装型的工厂&#xff0c;…

基于Boost和平均电流控制方法的APFC电路设计

通过学习无线充电相关知识&#xff0c;为更快熟悉APFC工作原理&#xff0c;通过实验得以掌握 技术要求&#xff1a; 1&#xff09;输入电压&#xff1a;AC 85V&#xff5e;265V&#xff1b; 2&#xff09;输出电压&#xff1a;400V1%&#xff1b; 3&#xff09;输出额定电流…

音视频开发30 FFmpeg 视频编码- 流程以及重要API,H264编码原理说明,该章节使用h264编码说明

一.H264编码原理 1 视频为什么需要进行编码压缩 ◼ 一张为 720x480 的图像&#xff0c;用 YUV420P 的格式来表示&#xff0c;其大小为&#xff1a; 720*480*1.5 约等于 0.5MB 。 ◼ 如果是 25 帧&#xff0c; 10 分钟的数据量 0.5M*10*60*25 7500MB -> 7GB 多 ◼ …

自学网络安全的三个必经阶段(含路线图)

一、为什么选择网络安全&#xff1f; 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 未来3-5年&#xff0c;是安全行业的黄金发展期&#xff0c;提前踏入…

blender 快捷键 常见问题

一、快捷键 平移视图&#xff1a;Shift 鼠标中键旋转视图&#xff1a;鼠标中键缩放视图&#xff1a;鼠标滚动框选放大模型&#xff1a;Shift B线框预览和材质预览切换&#xff1a;Shift Z 二、常见问题 问题&#xff1a;导入模型成功&#xff0c;但是场景中看不到。 解…

电路与数字逻辑期末复习重点整理!!

1.带无关项的卡诺图 2.置数法设计N进制电路 计数器&#xff1a;具有记忆输入脉冲个数功能的电路称为计数器。 按照各个触发器状态更新情况的不同可分为&#xff1a; 同步计数器&#xff1a;各触发器受同一时钟脉冲─输入计数脉冲控制&#xff0c;同步更新状态。异步计数器&a…

阿里云centos 7.9 使用宝塔面板部署.netcore 6.0

前言&#xff1a; 我有一个netcore6.0的系统接口和手机端程序的站点程序之前是部署在一台windows测试服务器的IIS站点中&#xff0c; 服务器最近压力太大扛不住了&#xff0c;买了一台centos7.9的阿里云服务器准备进行迁移。具体操作日记如下。 一、安装宝塔面板 这一步涉及…

堡垒机软件详细定义以及部分厂商汇总

随着大家对网络安全的重视&#xff0c;越来越多的企业开始采购堡垒机。堡垒机可以分为硬件堡垒机、软件堡垒机、软硬一体机。今天我们就来聊聊堡垒机软件详细定义以及部分厂商汇总。 堡垒机软件详细定义 堡垒机软件&#xff0c;又称为运维安全审计系统&#xff0c;其主要功能在…

顺序表--数据结构第一关

顺序表 数据结构概念 定义&#xff1a;数据结构是计算机存储、组织数据的⽅式 根据学过C语言的基础上&#xff0c;数组是最简单的数据结构 顺序表的底层就是数组 为什么呢&#xff1f; 例子如下&#xff1a; int arr[100]{1,2,3,4,5}; //修改某一个数据&#xff1a;arr[…

【Docker】docker 替换宿主与容器的映射端口和文件路径

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 docker 替换宿主与容器的映射端口和文件夹 1. 正文 1.1 关闭docker 服务 systemctl stop docker1.2 找到容器的配置文件 cd /var/lib/docker/contain…

游戏爱好者将《超级马里奥64》移植到GBA掌机

GBA虽然在当年拥有多款马里奥系列游戏&#xff0c;不过你一定没有想到&#xff0c;N64的《超级马里奥64》也能被移植到这个游戏掌机。近日&#xff0c;一位名为Joshua Barretto的开发者就完成了这一挑战。 大家都知道&#xff0c;《超级马里奥64》于1996年登陆任天堂64主机&am…

入职必备-mac下载安装maven

1、Maven 下载 1.1、官网下载安装包 官网下载链接 历史版本下载&#xff1a; Index of /dist/maven/maven-3/3.8.8/binaries 注意 .bash_profile 文件中的符号可能会影响配置 1.2、解压文件 2、Maven 环境配置 2.1、Java JDK 依赖 配置 maven 环境变量需要先配置好 JDK …