【问题标题】:java Modbus TCP SCADA/HMI works very slowjava Modbus TCP SCADA/HMI 工作很慢
【发布时间】:2015-04-07 16:40:02
【问题描述】:

我正在尝试用 Java 为简单的 SCADA/HMI 编写代码,以通过 Modbus TCP 将我的计算机连接到 PLC。我编写了代码来打开/关闭我的 PLC 上的 5 个线圈,但应用程序运行速度非常慢 - 当我按下按钮两次(这是打开/关闭线圈的条件)时,PLC 需要 4-6 秒才能获得命令。但我希望它能够快速工作。

在代码中,我编写了用于建立连接的主类和用于为每个线圈执行 ModBusTCPTransaction 的线程类。我将线程类称为 »Daemon« 并在主类中启动它。但也许这不是事情应该做的方法——也许任何人都可以写下这些类型的 SCADA/HMI 系统通常是如何完成/工作的,只需 2/3 句话……我什至需要一个守护线程类……?

这是我的代码

import java.net.*;
import java.io.*;
import net.wimpi.modbus.*;
import net.wimpi.modbus.msg.*;
import net.wimpi.modbus.io.*;
import net.wimpi.modbus.net.*;
import net.wimpi.modbus.util.*;
import java.awt.Color;
import java.awt.FlowLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics;

// 1. daemon class
class NitCoil extends Thread {

    private WriteCoilRequest coil_req = null;
    private ModbusTCPTransaction trans = null;
    private int i;

    NitCoil(String s , int i) {
        super(s);
        this.i = i;
    }

    public void run(){
        try {
        while(true) {
            coil_req = new WriteCoilRequest(i, ModbusTest.coil_con[i]);
            trans = new ModbusTCPTransaction(ModbusTest.con);
            trans.setRequest(coil_req);
            trans.execute();
            this.sleep((int)(Math.random()*100));
        }
        } catch (Exception ex) {
            ex.printStackTrace();
    }
    }

}
//2. main class
public class ModbusTest {

    public static TCPMasterConnection con = null;
    public static boolean[] coil_con = {false,false,false,false,false};
    public static void main(String[] args) {

      try {

          /* Variables for storing the parameters */
          InetAddress addr = null; //the slave's address
          int port = Modbus.DEFAULT_PORT;
          int repeat = 1; //a loop for repeating the transaction

          //3. Setup the parameters
          if (args.length < 1) {
              System.exit(1);
          } else {
              try {
                  String astr = "192.168.0.25:502"; 
                  int idx = astr.indexOf(':');
                  {
                      port = Integer.parseInt(astr.substring(idx+1));
                      astr = astr.substring(0,idx);
                  }
                  addr = InetAddress.getByName(astr);
                  if (args.length == 1) {
                      repeat = Integer.parseInt(args[0]);
                  }
              } catch (Exception ex) {
                  ex.printStackTrace();
                  System.exit(1);
              }
          }

          //4. Open the connection
          con = new TCPMasterConnection(addr);
          con.setPort(port);
          con.connect();

          //5. defining frame, panel, button
          JFrame frame = new JFrame("JFrame Example");
          JPanel panel = new JPanel();
          panel.setLayout(new FlowLayout());
          JLabel label = new JLabel("This is a label!");

          //6. creating 5 buttons             
          JButton[] button = new JButton[5];

          for (int j = 0; j < 5; j++){
               final int temp = j;
               button[j] = new JButton(String.valueOf(j));

          //7. button
          button[j].setText("Switch ON light "+j);

          button[j].addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                 if (coil_con[temp] == true) 
                     coil_con[temp] = false; 
                    else
                     coil_con[temp] = true;
            }
            });
          };
          panel.add(label);
          panel.add(button[0]);
          panel.add(button[1]);
          panel.add(button[2]);
          panel.add(button[3]);
          panel.add(button[4]);

          //8. call of demon
          NitCoil n1 = new NitCoil("daemon1", 0);
          n1.setDaemon(true);
          n1.start();
          NitCoil n2 = new NitCoil("daemon2", 1);
          n2.setDaemon(true);
          n2.start();
          NitCoil n3 = new NitCoil("daemon3", 2);
          n3.setDaemon(true);
          n3.start();
          NitCoil n4 = new NitCoil("daemon4", 3);
          n4.setDaemon(true);
          n4.start();
          NitCoil n5 = new NitCoil("daemon5", 4);
          n5.setDaemon(true);
          n5.start();


          //9. Close the connection
          JButton buttonClose = new JButton();
          buttonClose.setText("disconnect");

          buttonClose.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                 ModbusTest.con.close();
            }
            }); 
          panel.add(buttonClose);  
          panel.setBackground(Color.green);

          frame.add(panel);
          frame.setSize(300, 300);
          frame.setLocationRelativeTo(null);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setVisible(true);

      } catch (Exception ex) {
        ex.printStackTrace();
      }

    }  

}

【问题讨论】:

    标签: java multithreading tcp modbus


    【解决方案1】:

    您是否查看过 Wireshark 以了解响应时间?也许设备响应缓慢。

    此外,您目前的设置方式是,您一直在连续写入所有五个线圈,这可能并不理想。

    由于您的线圈地址是连续的,您还可以使用 WriteMultipleCoils 函数 (0x0F) 在一个请求中写入所有五个线圈。

    【讨论】:

    • 我现在使用了 WriteMultipleCoilsRequest 函数,现在 SCADA 工作得非常快。现在我正在无限循环中执行事务(daemon theread)——每 100 毫秒发生一次事务,这样可以吗,或者通常这类 SCADA 只执行一次事务——当我发生更改时(按下按钮).. ?保持寄存器怎么样,它们的事务是否也在无限循环中连续执行,或者它们仅在发生更改时执行(SCADA 上的十进制值被更改)..?
    • 您通常不会一遍又一遍地不断写入相同的值。读取值是另一回事......大多数工业协议没有任何订阅或未经请求的机制,因此使用轮询。
    • 什么是“polling”,我一直在网上搜索,但没有找到任何明确的解释——轮询是否意味着执行 modbus 事务..?那么读取值呢?轮询也用于它吗?
    • 轮询只是意味着一遍又一遍地阅读您感兴趣的值。我猜这个库会一遍又一遍地执行读取请求的事务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-01
    • 2019-11-27
    • 2014-06-07
    • 2010-11-13
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    相关资源
    最近更新 更多