java版连接汇川PLC,发送数据,读取数据,保持重新链接,适用安卓
公共类PlcTcpManager
java
复制代码
import static android.content.ContentValues.TAG;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class PlcTcpManager {
private final String ip;
private final int port;
private volatile boolean running = false;
private volatile boolean connected = false;
private Socket socket;
private InputStream inputStream;
private OutputStream outputStream;
private final Object lock = new Object();
public PlcTcpManager(String ip, int port) {
//this.context = context.getApplicationContext(); // 保存全局Context,防止内存泄漏
this.ip = ip;
this.port = port;
}
/* 启动后台通信线程(自动重连机制) */
public void start() {
running = true;
new Thread(this::connectionLoop, "PLC-Connect-Thread").start();
}
/* 停止通信并关闭连接 */
public void stop() {
running = false;
disconnect();
}
/* 主循环 */
private void connectionLoop() {
while (running) {
if (!connected) {
Log.i(TAG, "尝试连接PLC...");
connected = connect();
}
if (connected) {
// 维持心跳
if (!sendHeartbeat()) {
Log.i(TAG,"心跳失败,准备重连...");
connected = false;
disconnect();
}
}
// 等待3秒再检测
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
}
}
/* 尝试连接PLC */
private boolean connect() {
try {
socket = new Socket(ip, port);
socket.setSoTimeout(2000);
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
Log.i(TAG,"PLC连接成功");
return true;
} catch (IOException e) {
Log.e(TAG,"PLC连接失败: " + e.getMessage());
return false;
}
}
/** 断开连接 */
private void disconnect() {
try {
if (socket != null) socket.close();
} catch (IOException ignored) {
}
socket = null;
inputStream = null;
outputStream = null;
}
/** 心跳包发送 */
private boolean sendHeartbeat() {
try {
if (outputStream == null) return false;
// 示例:读D100命令
byte[] readCmd = new byte[]{
0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
0x01, 0x03, 0x00, 0x64, 0x00, 0x01
};
sendData(readCmd);
byte[] res = receiveData();
return res != null && res.length > 0;
} catch (Exception e) {
return false;
}
}
/** 发送任意数据 */
public boolean sendData(byte[] data) {
synchronized (lock) {
try {
if (outputStream == null) return false;
outputStream.write(data);
outputStream.flush();
return true;
} catch (IOException e) {
connected = false;
disconnect();
return false;
}
}
}
/** 接收数据 */
public byte[] receiveData() {
try {
if (inputStream == null) return null;
byte[] buffer = new byte[256];
int len = inputStream.read(buffer);
if (len > 0) {
byte[] result = new byte[len];
System.arraycopy(buffer, 0, result, 0, len);
return result;
}
} catch (IOException e) {
connected = false;
disconnect();
}
return null;
}
/* 读取多个寄存器(Modbus功能码) */
public int[] readRegisters(int startAddress, int count) {
if (!connected || outputStream == null || inputStream == null)
return null;
try {
// 构造Modbus TCP读命令
byte[] cmd = new byte[]{
0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
0x01, // 从站地址
0x03, // 功能码03:读保持寄存器
(byte) (startAddress >> 8), (byte) (startAddress & 0xFF),
(byte) (count >> 8), (byte) (count & 0xFF)
};
sendData(cmd);
byte[] res = receiveData();
if (res == null || res.length < 9) return null;
int byteCount = res[8] & 0xFF;
int[] values = new int[byteCount / 2];
for (int i = 0; i < values.length; i++) {
values[i] = ((res[9 + 2 * i] & 0xFF) << 8) | (res[10 + 2 * i] & 0xFF);
}
return values;
} catch (Exception e) {
e.printStackTrace();
connected = false;
disconnect();
return null;
}
}
// public int[] readRegisters(int startAddress, int count) {
// if (!connected || outputStream == null || inputStream == null)
// return null;
//
// try {
// // 构造读命令
// byte[] cmd = new byte[]{
// 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
// 0x01, // 从站地址
// 0x03, // 功能码 03 = 读保持寄存器
// (byte) (startAddress >> 8), (byte) (startAddress & 0xFF),
// (byte) (count >> 8), (byte) (count & 0xFF)
// };
//
// sendData(cmd);
// byte[] res = receiveData();
//
// if (res == null || res.length < 9) return null;
//
// int byteCount = res[8] & 0xFF;
// int[] values = new int[byteCount / 2];
// for (int i = 0; i < values.length; i++) {
// values[i] = ((res[9 + 2 * i] & 0xFF) << 8) | (res[10 + 2 * i] & 0xFF);
// }
// return values;
// } catch (Exception e) {
// e.printStackTrace();
// connected = false;
// disconnect();
// return null;
// }
// }
/** 判断是否连接 */
public boolean isConnected() {
return connected;
}
调用类
java
复制代码
/* PLC数据更新(每2秒刷新一次) */
PlcTcpManager plcManager = new PlcTcpManager("192.168.0.10", 502);
@Override
protected void onStart() {
super.onStart();
plcManager.start(); // 启动带自动重连的通信线程
}
@Override
protected void onStop() {
super.onStop();
plcManager.stop();
}
//点击按钮读取PLC数据
private void startPLCData() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
// 如果PLC已经连接上
if (plcManager != null && plcManager.isConnected()) {
new Thread(() -> {
Map<Integer, Integer> data = new HashMap<>();
int[] regs = plcManager.readRegisters(0, 4);
if (regs != null)
{
int sensorIndex = 1;
for (int reg : regs) {
for (int bit = 0; bit < 16; bit++) {
int bitValue = (reg >> bit) & 0x01; // 取出每一位
data.put(sensorIndex++, bitValue);
}
}
// 更新UI层(ViewModel)
//runOnUiThread(() -> sensorViewModel.updateAllSensors(data));
// 打印日志
saveToLog(data);
} else {
runOnUiThread(() -> Log.e("PLC", "读取PLC失败"));
}
}).start();
} else {
Log.w("PLC", "PLC未连接,等待重连...");
}
// 每2秒更新一次
handler.postDelayed(this, 2000);
}
}, 1000);
}
// private void startPLCData() {
// handler.postDelayed(new Runnable() {
// @Override
// public void run() {
// Map<Integer, Integer> data = new HashMap<>();
// //Integer status = 0;
// for (int i = 1; i <= 66; i++) {
// int statusType = random.nextInt(3);
// //status = statusType == 0 ? "正常" : statusType == 1 ? "报警" : "未知";
// data.put(i, statusType);
//
// }
// saveToLog(data);
// sensorViewModel.updateAllSensors(data);
// handler.postDelayed(this, 2000);
// }
// }, 1000);
// }
private void saveToLog(Map<Integer, Integer> data) {
try {
// ① 获取当前日期作为文件名
String date = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
String fileName = "plc_log_status" + date + ".txt";
// ② 文件路径:外部存储路径(可见)
File logFile = new File(getExternalFilesDir(null), fileName);
// ③ 如果文件不存在,先创建并写入文件头
if (!logFile.exists()) {
logFile.createNewFile();
FileWriter writer = new FileWriter(logFile, true);
writer.write("=== PLC 日志文件 " + date + " ===\n");
writer.write("时间\t\t传感器ID\t\t状态\n");
writer.close();
}
// ④ 记录一条新数据
FileWriter writer = new FileWriter(logFile, true);
String time = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(new Date());
for (Map.Entry<Integer, Integer> entry : data.entrySet()) {
writer.write(time + "\t\t" + entry.getKey() + "\t\t" + entry.getValue() + "\n");
}
writer.write("\n");
writer.flush();
writer.close();
} catch (IOException e) {
Log.e("PLC_LOG", "写入日志失败: " + e.getMessage());
}
}
/** 启动UDP监听线程 **/
private void startUdpListener(int port) {
new Thread(() -> {
try {
udpSocket = new DatagramSocket(port, InetAddress.getByName("0.0.0.0"));
udpSocket.setSoTimeout(0); // 永不超时
Log.i(TAG, "UDP 监听已启动, 端口 " + port);
byte[] buf = new byte[1024];
while (isListening) {
DatagramPacket packet = new DatagramPacket(buf, buf.length);
udpSocket.receive(packet);
InetAddress address = packet.getAddress();
String senderIP = address.getHostAddress();
int length = packet.getLength();
byte[] data = new byte[length];
System.arraycopy(packet.getData(), 0, data, 0, length);
// 尝试转成ASCII字符串
String ascii = new String(data, StandardCharsets.UTF_8).trim();
String hex = bytesToHex(data);
String cardNumber;
if (ascii.matches("[0-9A-Za-z]+")) {
cardNumber = ascii;
} else {
cardNumber = hex;
}
Log.i(TAG, "收到刷卡数据:" + cardNumber + " 来自 " + senderIP);
// 更新 ViewModel
runOnUiThread(() -> {
cardViewModel.setCardNumber(cardNumber);
if (!isReadCardVisible) {
bottomNav.setSelectedItemId(R.id.nav_read_card);
switchFragment(TAG_READ);
}
});
}
udpSocket.close();
} catch (Exception e) {
Log.e(TAG, "UDP监听异常", e);
}
}).start();
// new Thread(() -> {
// try {
// udpSocket = new DatagramSocket(port, InetAddress.getByName("0.0.0.0"));
// byte[] buffer = new byte[1024];
// while (isListening) {
// DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// udpSocket.receive(packet);
//
// String rawData = new String(packet.getData(), 0, packet.getLength()).trim();
// String cardNumber = decodeCardData(rawData);
//
// runOnUiThread(() -> {
// cardViewModel.setCardNumber(cardNumber);
//
// // 仅当不在刷卡界面时才跳转
// if (!isReadCardVisible) {
// bottomNav.setSelectedItemId(R.id.nav_read_card);
// switchFragment(TAG_READ);
// }
// });
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }).start();
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}