百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

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组件库的引用,并...

取消回复欢迎 发表评论: