package com.zehong.system.modbus.util;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * 独立的 多线程处理 多设备Modbus监控
 */
public class MultiDeviceModbusMonitor {
    private static final ModbusFactory modbusFactory = new ModbusFactory();

    // 配置参数
    private static final String HOST = "192.168.1.101";
    private static final int PORT = 501;
    private static final int START_ADDRESS = 0;  // 对应40001
    private static final int REGISTER_COUNT = 10; // 每个设备读取10个寄存器
    private static final int MAX_RETRIES = 3;    // 最大重试次数
    private static final int RETRY_DELAY = 500;  // 重试延迟(ms)
    private static final int MONITOR_INTERVAL = 2000; // 监控间隔(ms)
    private static final int DEVICE_COUNT = 4;  // 监控27个设备

    // 监控控制标志
    private static final AtomicBoolean monitoring = new AtomicBoolean(false);
    private static final AtomicInteger activeDevices = new AtomicInteger(0);

    // 设备统计信息
    private static final Map<Integer, DeviceStats> deviceStats = new HashMap<>();

    public static void main(String[] args) {
        try {
            System.out.println("===== 启动27设备监控系统 =====");
            System.out.printf("监控配置: %d设备 | %d寄存器/设备 | %dms间隔%n",
                    DEVICE_COUNT, REGISTER_COUNT, MONITOR_INTERVAL);

            // 初始化设备统计
            for (int i = 1; i <= DEVICE_COUNT; i++) {
                deviceStats.put(i, new DeviceStats());
            }

            // 创建结果处理器
            Consumer<int[]> resultHandler = data -> {
                // 实际应用中可添加业务逻辑
                // 这里只做简单演示
            };

            // 启动监控
            monitoring.set(true);
            startMultiDeviceMonitoring(resultHandler, values ->
                    values.length >= 2 && values[1] == 1);

            // 主线程等待监控结束
            System.out.println("\n主线程运行中...输入命令控制监控:");
            System.out.println("  status - 显示设备状态");
            System.out.println("  stop   - 停止所有监控");
            System.out.println("  exit   - 退出程序");

            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (monitoring.get()) {
                System.out.print("\n> ");
                String command = reader.readLine().trim().toLowerCase();

                switch (command) {
                    case "status":
                        printDeviceStatus();
                        break;
                    case "stop":
                        System.out.println("停止所有设备监控...");
                        monitoring.set(false);
                        break;
                    case "exit":
                        monitoring.set(false);
                        break;
                    default:
                        System.out.println("未知命令: " + command);
                }
            }

            // 等待所有监控线程结束
            waitForDevicesToStop();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("监控系统已关闭");
        }
    }

    /**
     * 启动多设备监控
     */
    public static void startMultiDeviceMonitoring(Consumer<int[]> resultHandler,
                                                  Predicate<int[]> stopCondition) {
        System.out.println("启动设备监控线程...");

        // 使用线程池管理27个设备线程
        ExecutorService executor = Executors.newFixedThreadPool(DEVICE_COUNT);

        for (int deviceId = 1; deviceId <= DEVICE_COUNT; deviceId++) {
            final int devId = deviceId;
            executor.submit(() -> {
                activeDevices.incrementAndGet();
                try {
                    startDeviceMonitor(devId, resultHandler, stopCondition);
                } finally {
                    activeDevices.decrementAndGet();
                }
            });
        }

        // 关闭线程池接受新任务，但不影响已提交任务
        executor.shutdown();
    }

    /**
     * 启动单个设备监控
     */
    public static void startDeviceMonitor(int deviceId, Consumer<int[]> resultHandler,
                                          Predicate<int[]> stopCondition) {
        ModbusMaster threadMaster = null;
        try {
            // 1. 创建线程专用的Modbus连接
            threadMaster = createModbusMaster();
            DeviceStats stats = deviceStats.get(deviceId);
            stats.setStatus("运行中");

            System.out.printf("设备 %02d: 监控启动 [%s]%n",
                    deviceId, Thread.currentThread().getName());

            int cycle = 0;

            while (monitoring.get()) {
                cycle++;
                stats.incrementReadCount();

                // 2. 读取数据（带重试）
                int[] result = readWithConditionalRetry(threadMaster, deviceId, stats);

                // 3. 更新统计信息
                if (result != null && result.length >= 2) {
                    stats.updateLastValue(result[1]);
                }

                // 4. 回调处理结果
                resultHandler.accept(result);

                // 5. 检查是否应该停止监控
                if (stopCondition.test(result)) {
                    stats.setStatus("条件满足停止");
                    stats.setConditionMet(true);
                    System.out.printf("设备 %02d: ✅ 停止条件满足%n", deviceId);
                    break;
                }

                // 6. 监控间隔
                try {
                    Thread.sleep(MONITOR_INTERVAL);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        } catch (Exception e) {
            System.err.printf("设备 %02d 监控异常: %s%n", deviceId, e.getMessage());
            deviceStats.get(deviceId).setStatus("异常: " + e.getClass().getSimpleName());
        } finally {
            // 7. 关闭线程专用的Modbus连接
            if (threadMaster != null) {
                threadMaster.destroy();
            }
            if (!deviceStats.get(deviceId).isConditionMet()) {
                deviceStats.get(deviceId).setStatus("手动停止");
            }
            System.out.printf("设备 %02d: 监控结束%n", deviceId);
        }
    }

    /**
     * 打印设备状态
     */
    private static void printDeviceStatus() {
        System.out.println("\n设备状态概览:");
        System.out.println("+-------+-----------+-----------+-----------+-----------------+---------+");
        System.out.println("| 设备ID| 读取次数  | 成功次数  | 失败次数  | 最后值(寄存器2) | 状态    |");
        System.out.println("+-------+-----------+-----------+-----------+-----------------+---------+");

        for (int i = 1; i <= DEVICE_COUNT; i++) {
            DeviceStats stats = deviceStats.get(i);
            System.out.printf("| %5d | %9d | %9d | %9d | %15d | %-7s |%n",
                    i,
                    stats.getReadCount(),
                    stats.getSuccessCount(),
                    stats.getErrorCount(),
                    stats.getLastValue(),
                    stats.getStatus());
        }
        System.out.println("+-------+-----------+-----------+-----------+-----------------+---------+");
        System.out.printf("活动设备: %d/%d%n", activeDevices.get(), DEVICE_COUNT);
    }

    /**
     * 等待所有设备停止
     */
    private static void waitForDevicesToStop() {
        System.out.print("等待设备停止");
        int waitCount = 0;
        while (activeDevices.get() > 0 && waitCount < 30) {
            System.out.print(".");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            waitCount++;
        }
        System.out.println();

        if (activeDevices.get() > 0) {
            System.out.printf("警告: %d个设备未正常停止%n", activeDevices.get());
        }
    }

    /**
     * 创建新的Modbus连接
     */
    private static ModbusMaster createModbusMaster() throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost(HOST);
        params.setPort(PORT);

        ModbusMaster master = modbusFactory.createTcpMaster(params, true);
        master.setTimeout(3000);
        master.setRetries(1);
        master.init();

        return master;
    }

    /**
     * 带自定义条件的重试读取
     */
    private static int[] readWithConditionalRetry(ModbusMaster master, int deviceId,
                                                  DeviceStats stats) {

        int[] result = null;
        int attempt = 0;
        boolean success = false;

        while (attempt <= MAX_RETRIES && monitoring.get()) {
            attempt++;
            try {
                // 读取数据
                result = readDeviceRegisters(master, deviceId, START_ADDRESS, REGISTER_COUNT);
                stats.incrementSuccessCount();
                success = true;
                break;
            } catch (Exception e) {
                stats.incrementErrorCount();
                System.err.printf("设备 %02d 尝试 %d/%d 失败: %s%n",
                        deviceId, attempt, MAX_RETRIES, e.getMessage());

                if (attempt <= MAX_RETRIES) {
                    try {
                        Thread.sleep(RETRY_DELAY);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }

        if (!success) {
            System.err.printf("设备 %02d: ⚠️ 达到最大重试次数(%d)%n", deviceId, MAX_RETRIES);
        }

        return result != null ? result : new int[REGISTER_COUNT];
    }

    /**
     * 读取设备寄存器
     */
    private static int[] readDeviceRegisters(ModbusMaster master, int deviceId,
                                             int startAddress, int count)
            throws ModbusTransportException, ErrorResponseException {

        // 创建读取请求
        ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(
                deviceId, startAddress, count
        );

        // 发送请求并获取响应
        ModbusResponse response = master.send(request);

        // 检查响应类型
        if (!(response instanceof ReadHoldingRegistersResponse)) {
            throw new IllegalArgumentException("Invalid response type: " + response.getClass().getName());
        }

        ReadHoldingRegistersResponse regResponse = (ReadHoldingRegistersResponse) response;
        short[] signedValues = regResponse.getShortData();

        // 转换为无符号整数
        int[] unsignedValues = new int[signedValues.length];
        for (int i = 0; i < signedValues.length; i++) {
            unsignedValues[i] = signedValues[i] & 0xFFFF;
        }

        return unsignedValues;
    }

    /**
     * 设备统计信息类
     */
    static class DeviceStats {
        private int readCount = 0;
        private int successCount = 0;
        private int errorCount = 0;
        private int lastValue = -1;
        private String status = "未启动";
        private boolean conditionMet = false;

        public void incrementReadCount() {
            readCount++;
        }

        public void incrementSuccessCount() {
            successCount++;
        }

        public void incrementErrorCount() {
            errorCount++;
        }

        public void updateLastValue(int value) {
            this.lastValue = value;
        }

        // Getter 和 Setter 方法
        public int getReadCount() { return readCount; }
        public int getSuccessCount() { return successCount; }
        public int getErrorCount() { return errorCount; }
        public int getLastValue() { return lastValue; }
        public String getStatus() { return status; }
        public void setStatus(String status) { this.status = status; }
        public boolean isConditionMet() { return conditionMet; }
        public void setConditionMet(boolean conditionMet) {
            this.conditionMet = conditionMet;
        }
    }
}