java版连接汇川PLC,发送数据,读取数据,保持重新链接,适用安卓

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();
    }
相关推荐
zzzzls~14 小时前
Python 工程化: 用 Copier 打造“自我进化“的项目脚手架
开发语言·python·copier
言慢行善14 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
韶博雅15 小时前
emcc24ai
开发语言·数据库·python
专吃海绵宝宝菠萝屋的派大星15 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟15 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z15 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可15 小时前
Java 中的实现类是什么
java·开发语言
He少年15 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
AI_Claude_code15 小时前
ZLibrary访问困境方案四:利用Cloudflare Workers等边缘计算实现访问
javascript·人工智能·爬虫·python·网络爬虫·边缘计算·爬山算法
克里斯蒂亚诺更新15 小时前
myeclipse的pojie
java·ide·myeclipse