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