Search code examples
javamultithreadingtcpmodbus

java Modbus TCP SCADA/HMI works very slow


I am trying to write code in Java for a simple SCADA/HMI to connect my computer to a PLC via Modbus TCP. I wrote the code to switch ON/OFF 5 coils on my PLC, but the application works very slow – when I press the button twice (which is the condition to swich ON/OFF the coil) the PLC takes 4-6 seconds to get the command. But I want it to work quickly.

In the code I wrote the main class, in which I establish the connection, and the thread class, in which I execute the ModBusTCPTransaction for each coil. I call the thread class as »Daemon« and start it in main class. But maybe this is not the approach that things should be done – maybe anyone can write down how usually tese type of SCADA/HMI systems are done/work, just in 2/3 sentences… Do I even need a deamon thread class…?

Here is my code

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();
      }

    }  

}

Solution

  • Have you looked at Wireshark to see what the response times are like? Maybe the device is slow to respond.

    Also, the way you currently have this set up, you're continuously writing all five coils all the time, which is probably not ideal.

    Since your coil addresses are contiguous you could also be using the WriteMultipleCoils function (0x0F) to write to all five coils in one request.