您的位置 首页 java

Java 通过 jlibmodbus 架包和PLC通讯

最近项目上涉及到一些硬件设备,这些硬件设备是通过 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 服务

Java 通过 jlibmodbus 架包和PLC通讯

2、点击程序块,右键选择“库存储器”,建议从1000开始,防止地址冲突

Java 通过 jlibmodbus 架包和PLC通讯

Java 通过 jlibmodbus 架包和PLC通讯

3、设置你要控制的点,比喻说我要控制第1台电机和第3台电机启动,那我就在第1台电机设置一个 V101.0 的保持寄存器,在第3台电机设置一个 V105.0 的保持寄存器,并且都设置为常开状态

Java 通过 jlibmodbus 架包和PLC通讯

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 (Modbus Protocol Exception 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,便于借鉴一二。

文章来源:智云一二三科技

文章标题:Java 通过 jlibmodbus 架包和PLC通讯

文章地址:https://www.zhihuclub.com/169283.shtml

关于作者: 智云科技

热门文章

网站地图