package com.zehong.system.modbus.util;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
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.ip.tcp.TcpMaster;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import com.zehong.system.domain.vo.WriteCurrentTimeVo;
import com.zehong.system.modbus.handler.ModbusResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * modbus通讯工具类,采用modbus4j实现
 *
 */
@SuppressWarnings(value = "all")
public class Modbus4jUtils {

    private static final Logger log = LoggerFactory.getLogger(Modbus4jUtils.class);

    private static ModbusMaster master;

    private static final int START_ADDRESS = 0;  // 对应40001
    private static final int REGISTER_COUNT = 16; // 读取15个寄存器
    private static final int REGISTER_COUNT_36 = 36; // 读取36个寄存器
    private static final int TARGET_VALUE = 1;   // 目标值（第二个寄存器的期望值）
    private static final int MAX_RETRIES = 3;    // 最大重试次数
    private static final int RETRY_DELAY = 500;  // 重试延迟(ms)// 监控参数
    private static final int MONITOR_INTERVAL = 5000; // 监控间隔(ms)
    // Modbus配置：取消内置重试，统一用自定义重试
    private static final int MODBUS_CONN_TIMEOUT_MS = 3000; // 连接超时：3秒
    private static final int CUSTOM_RETRY_TIMES = 2; // 自定义重试次数：1次

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

    private static final Lock masterLock = new ReentrantLock();
    /**
     * 工厂。
     */
    static ModbusFactory modbusFactory;
    static {
        if (modbusFactory == null) {
            modbusFactory = new ModbusFactory();
        }
    }
    /**
     * 获取master
     *
     * @return master
     * @throws ModbusInitException m
     */
    public static ModbusMaster getMaster() throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost("192.168.2.11");
        params.setPort(502);
        //
        // modbusFactory.createRtuMaster(wapper); //RTU 协议
        // modbusFactory.createUdpMaster(params);//UDP 协议
        // modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
        if(master == null || !master.isConnected()) {
            master = modbusFactory.createTcpMaster(params, true);// TCP 协议
            master.setTimeout(5000); // 设置超时时间
            master.setRetries(3);    // 设置重试次数
            master.init();
        }

        return master;
    }

    // -------------------------- Modbus工具方法（显式抛出异常）--------------------------
    /**
     * 创建Modbus连接（取消内置重试，统一自定义重试）
     */
    public static ModbusMaster createModbusMaster(String ip, int port) throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost(ip);
        params.setPort(port);

        TcpMaster master = (TcpMaster) modbusFactory.createTcpMaster(params, true);
        master.setTimeout(MODBUS_CONN_TIMEOUT_MS);
        master.setRetries(0);
        master.init();
        return master;
    }

    /**
     * 销毁Modbus连接（反射失败直接抛出异常，显式暴露问题）
     */
    public static void destroyModbusMaster(ModbusMaster master, int deviceId) {
        if (master != null) {
            try {
                master.destroy();
            } catch (Exception e) {
                log.debug("设备{}: ModbusMaster销毁异常", deviceId, e);
            }
        }
    }
    /**
     * 读取[01 Coil Status 0x]类型 开关数据
     *
     * @param slaveId
     *            slaveId
     * @param offset
     *            位置
     * @return 读取值
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Boolean readCoilStatus(int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 01 Coil Status
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
        Boolean value = getMaster().getValue(loc);
        return value;
    }

    /**
     * 读一下 开关数据
     * @param master
     * @param slaveId
     * @param offset
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static Boolean readCoilStatus(ModbusMaster master, int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 01 Coil Status
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
        Boolean value = master.getValue(loc);
        return value;
    }

    /**
     * 读取[02 Input Status 1x]类型 开关数据
     *
     * @param slaveId
     * @param offset
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static Boolean readInputStatus(int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 02 Input Status
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
        Boolean value = getMaster().getValue(loc);
        return value;
    }

    /**
     * 读取[02 Input Status 1x]类型 开关数据
     *
     * @param slaveId
     * @param offset
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static Boolean readInputStatus(ModbusMaster master,int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 02 Input Status
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
        Boolean value = master.getValue(loc);
        return value;
    }

    /**
     * 机械臂入口传送带 0位置是 投入点，1位置是 末尾点
     * @param slaveId
     * @param registerOffsets
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     */
    public static boolean[] getRoboticArmEntryConveyorData(){

        ModbusMaster master = null;
        try {
            master = createModbusMaster("192.168.1.11", 503);
            boolean[] booleans = readDiscreteInputs(master, 1, 0, 2);

            if(master != null) {
                master.destroy();
            }

            return booleans;
        } catch (ModbusTransportException | ModbusInitException e) {
            return  new boolean[]{false, false}; // 示例数据
        } finally {
            if(master != null) {
                master.destroy();
            }
        }
    }

    /**
     * 机械臂出库传送带 0位置是 投入点，1位置是 末尾点
     * @param slaveId
     * @param registerOffsets
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     */
    public static boolean[] getRoboticArmExitConveyorData(String ip, int port) {
        ModbusMaster master = null;
        try {
            master = createModbusMaster(ip, port);
            boolean[] booleans = readDiscreteInputs(master, 1, 0, 2);

            if(master != null) {
                master.destroy();
            }

            return booleans;
        } catch (ModbusTransportException | ModbusInitException e) {
            return  new boolean[]{false, false}; // 示例数据
        } finally {
            if(master != null) {
                master.destroy();
            }
        }
    }

    /**
     * 读取离散输入（功能码02）的兼容方法
     * 使用ReadDiscreteInputsRequest/Response，适配更多版本
     */
    private static boolean[] readDiscreteInputs(ModbusMaster master, int slaveId,
                                                int startAddress, int count) throws ModbusTransportException {
        // 创建功能码02对应的请求（ReadDiscreteInputsRequest）
        ModbusRequest request = new ReadDiscreteInputsRequest(slaveId, startAddress, count);

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

        // 转换为具体的响应类型
        if (response instanceof ReadDiscreteInputsResponse) {
            ReadDiscreteInputsResponse discreteResponse = (ReadDiscreteInputsResponse) response;
            return discreteResponse.getBooleanData();
        } else {
            throw new ModbusTransportException("无效的响应类型: " + response.getClass().getSimpleName());
        }
    }
    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     *
     * @param slaveId
     *            slave Id
     * @param offset
     *            位置
     * @param dataType
     *            数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Number readHoldingRegister(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        Number value = getMaster().getValue(loc);
        return value;
    }

    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param slaveId
     *            slaveId
     * @param offset
     *            位置
     * @param dataType
     *            数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Number readInputRegisters(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        Number value = getMaster().getValue(loc);
        return value;
    }

    /**
     *
     * @param master
     * @param slaveId
     * @param offset
     * @param length
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param master
     *          master
     * @param slaveId
     *            slaveId
     * @param offset
     *            位置
     * @param dataType
     *            数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Number readInputRegisters(ModbusMaster master,int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        Number value = master.getValue(loc);
        return value;
    }

    /**
     * 读老化柜的 10层数据
     * @param master
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static Map<Integer, Object>  readInputRegistersForAgeng(ModbusMaster master)
            throws ModbusTransportException, ErrorResponseException {
        // 10 层
        List<Integer> registerOffsets = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
        Map<Integer, Object> result = new HashMap<>();
        for (Integer registerOffset : registerOffsets) {
            // 04 Input Registers类型数据读取
            BaseLocator<Number> loc = BaseLocator.inputRegister(1, registerOffset, DataType.TWO_BYTE_INT_UNSIGNED);
            Number value = master.getValue(loc);
            result.put(registerOffset, value);
        }
        return result;
    }

    /**
     * 批量读取使用方法
     *
     * @throws ModbusTransportException m
     * @throws ErrorResponseException e
     * @throws ModbusInitException m
     */
    public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        BatchRead<Integer> batch = new BatchRead<Integer>();

        batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
        batch.addLocator(1, BaseLocator.inputStatus(1, 0));

        ModbusMaster master = getMaster();

        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        System.out.println(results.getValue(0));
        System.out.println(results.getValue(1));
    }

    public static Map<Integer,Object> batchReadHoldingRegister(ModbusMaster master,List<Integer> registerOffsets) throws ModbusTransportException, ErrorResponseException {
        BatchRead<Integer> batch = new BatchRead<Integer>();
        for (Integer registerOffset : registerOffsets) {
            batch.addLocator(registerOffset, BaseLocator.holdingRegister(1, registerOffset,DataType.EIGHT_BYTE_INT_UNSIGNED));
        }

        // 非连续地址也支持
        batch.setContiguousRequests(true);

        BatchResults<Integer> send = master.send(batch);
//
        Map<Integer, Object> result = new HashMap<>();
        for (Integer registerOffset : registerOffsets) {
            result.put(registerOffset, send.getValue(registerOffset));
        }
        return result;
    }


    /**
     * 批量读取使用方法
     *
     * @throws ModbusTransportException m
     * @throws ErrorResponseException e
     * @throws ModbusInitException m
     */
    public static Map<Integer, Object> batchReadAgingCabinetStatus(ModbusMaster master, List<Integer> registerOffsets) throws ModbusInitException, ModbusTransportException, ErrorResponseException {

        // 把设备集成到一个交换机上后 这个批量读取 不管用了。。
        BatchRead<Integer> batch = new BatchRead<Integer>();
        for (Integer registerOffset : registerOffsets) {
            batch.addLocator(registerOffset, BaseLocator.inputStatus(1, registerOffset));
        }

        // 非连续地址也支持
        batch.setContiguousRequests(true);

        BatchResults<Integer> send = master.send(batch);
        Map<Integer, Object> result = new HashMap<>();
        for (Integer registerOffset : registerOffsets) {
            result.put(registerOffset, send.getValue(registerOffset));
        }
        return result;

//        Map<Integer, Object> result = new HashMap<>();
//        for (Integer registerOffset : registerOffsets) {
//            Boolean aBoolean = Modbus4jUtils.readCoilStatus(master, 1, registerOffset);
//            result.put(registerOffset, aBoolean);
//        }
//        return result;
    }

    /**
     * 读取保持寄存器（16位无符号整数）
     *
     * @param slaveId  从站ID
     * @param offset   寄存器起始地址（0-based）
     * @param count    读取的寄存器数量
     * @return 无符号整数值数组
     * 请求格式解析：
     * 00 00 - 事务ID (0)
     * 00 00 - 协议ID (0)
     * 00 06 - 长度 (6字节)
     * 01 - 从站ID (1)
     * 03 - 功能码 (读保持寄存器)
     * 00 03 - 起始地址 (3)
     * 00 0A - 寄存器数量 (10)
     * 响应格式解析：
     * 00 00 00 00 00 17 - Modbus TCP头
     * 01 - 从站ID
     * 03 - 功能码
     * 14 - 数据字节数 (20字节 = 10寄存器 × 2字节)
     * 后续20字节为10个寄存器的数据
     * 关键处理逻辑：
     * 16位无符号转换：register.getValue() & 0xFFFF
     * 寄存器地址处理：modbus4j使用0-based地址，所以地址3对应设备手册中的40004寄存器
     * TCP连接管理：使用长连接提高效率
     */
    public static int[] readHoldingRegisters(int slaveId, int offset, int count)
            throws ModbusTransportException, ErrorResponseException {

        // 创建读取请求
        ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(
                slaveId, offset, 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[] results = new int[signedValues.length];
        // 转为无符号整数并打印
        for (int i = 0; i < signedValues.length; i++) {
            int unsignedValue = signedValues[i] & 0xFFFF;
            results[i] = signedValues[i] & 0xFFFF;
            System.out.printf("寄存器 %d: %d (无符号) %n", i, unsignedValue);
        }
        return results;
    }

    /**
     * 写 [01 Coil Status(0x)]写一个 function ID = 5
     *
     * @param slaveId
     *            slave的ID
     * @param writeOffset
     *            位置
     * @param writeValue
     *            值
     * @return 是否写入成功
     * @throws ModbusTransportException m
     * @throws ModbusInitException m
     */
    public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)
            throws ModbusTransportException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster();
        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
        // 发送请求并获取响应对象
        WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);
        if (response.isException()) {
            return false;
        } else {
            return true;
        }
    }
    /**
     * 写[01 Coil Status(0x)] 写一个 function ID = 5
     * @param master
     * @param slaveId
     * @param writeOffset
     * @param writeValue
     * @return
     * @throws ModbusTransportException
     */
    public static boolean writeCoil(ModbusMaster master, int slaveId, int writeOffset, boolean writeValue) throws ModbusTransportException {
        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
        // 发送请求并获取响应对象
        WriteCoilResponse response = (WriteCoilResponse) master.send(request);
        if (response.isException()) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 写[01 Coil Status(0x)] 写多个 function ID = 15
     *
     * @param slaveId
     *            slaveId
     * @param startOffset
     *            开始位置
     * @param bdata
     *            写入的数据
     * @return 是否写入成功
     * @throws ModbusTransportException m
     * @throws ModbusInitException m
     */
    public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)
            throws ModbusTransportException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster();
        // 创建请求
        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
        if (response.isException()) {
            return false;
        } else {
            return true;
        }

    }
    /**
     * 写入单个 Holding Register（功能码 6）
     *
     * @param slaveId     从站ID
     * @param writeOffset 寄存器偏移地址（如 0 表示地址 40001）
     * @param writeValue  要写入的值（short 类型）
     * @return 是否写入成功
     * @throws ModbusTransportException 如果传输失败
     * @throws ModbusInitException      如果初始化失败
     */
    public static boolean writeRegister(ModbusMaster master ,int slaveId, int writeOffset, short writeValue)
            throws ModbusTransportException, ModbusInitException {
        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);

        log.info("发送写入请求：从站={}, 地址={}, 值={}", slaveId, writeOffset, writeValue);

        WriteRegisterResponse response = null;
        synchronized (master) { // 多线程下保护主站资源
            response = (WriteRegisterResponse) master.send(request);
        }

        if (response == null) {
            log.error("未收到 Modbus 响应");
            return false;
        }

        if (response.isException()) {
            log.error("Modbus 异常响应: {}", response.getExceptionMessage());
            return false;
        }

        int writeValue1 = response.getWriteValue();

        log.info("写入成功，写入值={}", writeValue1);

        log.info("写入成功，响应确认");
        return true;
    }
    /**
     * 写入单个 Holding Register（功能码 6）
     *
     * @param slaveId     从站ID
     * @param writeOffset 寄存器偏移地址（如 0 表示地址 40001）
     * @param writeValue  要写入的值（short 类型）
     * @return 是否写入成功
     * @throws ModbusTransportException 如果传输失败
     * @throws ModbusInitException      如果初始化失败
     */
    public static boolean writeRegister(int slaveId, int writeOffset, short writeValue)
            throws ModbusTransportException, ModbusInitException {
        ModbusMaster master = getMaster();

        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);

        log.info("发送写入请求：从站={}, 地址={}, 值={}", slaveId, writeOffset, writeValue);

        WriteRegisterResponse response = null;
        synchronized (master) { // 多线程下保护主站资源
            response = (WriteRegisterResponse) master.send(request);
        }

        if (response == null) {
            log.error("未收到 Modbus 响应");
            return false;
        }

        if (response.isException()) {
            log.error("Modbus 异常响应: {}", response.getExceptionMessage());
            return false;
        }

        log.info("写入成功，响应确认");
        return true;
    }

    /**
     * 批量写入保持寄存器（功能码16）
     *
     * @param master Modbus主站实例
     * @param slaveId 从站ID
     * @param startOffset 起始寄存器偏移地址
     * @param values 要写入的值数组
     * @return 写入是否成功
     * @throws ModbusTransportException 传输异常
     * @throws ModbusInitException 初始化异常
     */
    public static boolean writeRegisters(ModbusMaster master, int slaveId,
                                         int startOffset, short[] values)
            throws ModbusTransportException, ModbusInitException {

        if (values == null || values.length == 0) {
            log.warn("写入值为空，不执行写入操作");
            return false;
        }

        if (values.length > 123) { // Modbus协议限制
            log.error("批量写入数量超出限制：{} > 123", values.length);
            return false;
        }

        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, values);

        log.info("发送批量写入请求：从站={}, 起始地址={}, 数量={}",
                slaveId, startOffset, values.length);

        WriteRegistersResponse response = null;
        boolean lockAcquired = false;

        try {
            // 尝试获取锁，最多等待500ms
            lockAcquired = masterLock.tryLock(500, TimeUnit.MILLISECONDS);

            if (!lockAcquired) {
                log.warn("获取Modbus主站锁超时，从站={}", slaveId);
                return false;
            }

            // 发送请求
            response = (WriteRegistersResponse) master.send(request);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("获取锁时被中断", e);
            return false;
        } finally {
            if (lockAcquired) {
                masterLock.unlock();
            }
        }

        return processBatchResponse(response, slaveId, startOffset, values);
    }

    /**
     * 处理批量写入响应
     */
    private static boolean processBatchResponse(WriteRegistersResponse response,
                                                int slaveId, int startOffset, short[] values) {
        if (response == null) {
            log.error("未收到Modbus批量写入响应，从站={}, 起始地址={}", slaveId, startOffset);
            return false;
        }

        if (response.isException()) {
            log.error("Modbus批量写入异常响应，从站={}, 起始地址={}, 异常码={}",
                    slaveId, startOffset, response.getExceptionCode());
            return false;
        }

        log.info("批量写入成功：从站={}, 起始地址={}, 数量={}",
                slaveId, startOffset, values.length);
        return true;
    }

    /**
     * 批量写入整型数组
     */
    public static boolean writeIntRegisters(ModbusMaster master, int slaveId,
                                            int startOffset, int[] intValues)
            throws ModbusTransportException, ModbusInitException {

        if (intValues == null || intValues.length == 0) {
            return false;
        }

        // 将int转换为short（每个int占2个寄存器）
        short[] values = new short[intValues.length * 2];

        for (int i = 0; i < intValues.length; i++) {
            // 高位在前
            values[i * 2] = (short) ((intValues[i] >> 16) & 0xFFFF);
            values[i * 2 + 1] = (short) (intValues[i] & 0xFFFF);
        }

        return writeRegisters(master, slaveId, startOffset, values);
    }

    /**
     * 使用功能码16写入多个保持寄存器（更稳定，支持批量）
     */
    public static boolean writeRegistersMultiple(ModbusMaster master, int slaveId,
                                                 int startOffset, short[] values)
            throws ModbusTransportException, ModbusInitException {

        log.info("发送批量写入请求：从站={}, 起始地址={}, 数量={}, 值={}",
                slaveId, startOffset, values.length, Arrays.toString(values));

        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, values);
        WriteRegistersResponse response = null;

        synchronized (master) {
            response = (WriteRegistersResponse) master.send(request);
        }

        if (response == null) {
            log.error("未收到 Modbus 响应");
            return false;
        }

        if (response.isException()) {
            log.error("Modbus 异常响应: {}", response.getExceptionMessage());
            return false;
        }

        log.info("批量写入成功，起始地址={}, 写入数量={}", startOffset, values.length);
        return true;
    }

    /**
     * 写入时间（使用功能码16一次性写入所有时间寄存器）
     */
    public static boolean writeTimeRegisters(ModbusMaster master, int deviceId) {
        try {
            Calendar cal = Calendar.getInstance();
            short[] timeValues = {
                    (short) cal.get(Calendar.YEAR),
                    (short) (cal.get(Calendar.MONTH) + 1),
                    (short) cal.get(Calendar.DATE),
                    (short) cal.get(Calendar.HOUR_OF_DAY),
                    (short) cal.get(Calendar.MINUTE)
            };

            // 一次性写入所有时间寄存器
            return writeRegistersMultiple(master, deviceId, 4, timeValues);

        } catch (Exception e) {
            log.error("批量写入时间失败", e);
            return false;
        }
    }
    /***
     * 写[03 Holding Register(4x)] 写一个 function ID = 6
     *
     * @param slaveId s
     * @param writeOffset w
     * @param writeValue w
     * @return
     * @throws ModbusTransportException m
     * @throws ModbusInitException m
     */
//    public static boolean writeRegister(int slaveId, int writeOffset, short writeValue)
//            throws ModbusTransportException, ModbusInitException {
//        // 获取master
//        ModbusMaster tcpMaster = getMaster();
//        // 创建请求对象
//        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
//        WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
//        if (response.isException()) {
//            log.error(response.getExceptionMessage());
//            return false;
//        } else {
//            return true;
//        }
//
//    }

    /**
     *
     * 写入[03 Holding Register(4x)]写多个 function ID=16
     *
     * @param slaveId
     *            modbus的slaveID
     * @param startOffset
     *            起始位置偏移量值
     * @param sdata
     *            写入的数据
     * @return 返回是否写入成功
     * @throws ModbusTransportException m
     * @throws ModbusInitException m
     */
    public static boolean writeRegisters(int slaveId, int startOffset, short[] sdata)
            throws ModbusTransportException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster();
        // 创建请求对象
        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
        // 发送请求并获取响应对象
        ModbusResponse response = tcpMaster.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }



    /**
     * 写入数字类型的模拟量（如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long）
     *
     * @param slaveId s
     * @param offset o
     * @param value
     *            写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
     * @param dataType r
     *            ,com.serotonin.modbus4j.code.DataType
     * @throws ModbusTransportException m
     * @throws ErrorResponseException e
     * @throws ModbusInitException m
     */
    public static void writeHoldingRegister(int slaveId, int offset, Number value, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster();
        // 类型
        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
        tcpMaster.setValue(locator, value);
    }

    /**
     * 关闭Modbus连接
     */
    public static void destroyMaster() {
        if (master != null) {
            master.destroy();
            System.out.println("Modbus连接已关闭");
            master = null;
        }
    }

    /**
     * 获取读取Holding Register请求对象
     *
     * @param slaveId
     * @param offset
     * @param count
     * @return
     * @throws ModbusTransportException
     */
    public static ReadHoldingRegistersRequest getReadHoldingRegistersRequest(int slaveId, int offset, int count) throws ModbusTransportException {
        return new ReadHoldingRegistersRequest(slaveId, offset, count);
    }
    /**
     * 读取设备寄存器（线程安全版）
     */
    private static int[] readDeviceRegisters(int deviceId )
            throws ModbusTransportException, ModbusInitException {

        ModbusMaster master = getMaster();
        // 创建读取请求
        ReadHoldingRegistersRequest request = Modbus4jUtils.getReadHoldingRegistersRequest(deviceId,START_ADDRESS, REGISTER_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;
    }



    /**
     * 测试
     *
     * @param args a
     */
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        ModbusMaster modbusMaster = null;
        try {
            // 读取 第1个 pcba 板子的数据
//            modbusMaster = createModbusMaster("192.168.2.1", 501);
//            writeCurrentTimeUseWriteRegisters(modbusMaster, 1);
//            writeCurrentTimeToDeviceEnhanced(modbusMaster,1);
//            writeTimeRegisters(modbusMaster, 1);
            int[] ints = readDeviceWithRetry("192.168.2.1", 501, 1);
            log.info("ints[0]={}", ints[0]);
            log.info("ints[1]={}", ints[1]);
            log.info("ints[2]={}", ints[2]);
            log.info("ints[3]={}", ints[3]);
            log.info("ints[4]={}", ints[4]);
            log.info("ints[5]={}", ints[5]);
            log.info("ints[6]={}", ints[6]);
            log.info("ints[7]={}", ints[7]);
            log.info("ints[8]={}", ints[8]);
            log.info("ints[9]={}", ints[9]);
            log.info("ints[10]={}", ints[10]);
            log.info("ints[11]={}", ints[11]);
            log.info("ints[12]={}", ints[12]);
            log.info("ints[13]={}", ints[13]);
            log.info("ints[14]={}", ints[14]);
            log.info("ints[15]={}", ints[15]);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (modbusMaster != null) {
                modbusMaster.destroy();
                modbusMaster = null;
            }
        }
        log.info("任务执行成功:, 耗时={}ms",  System.currentTimeMillis() - startTime);
    }
    /**
     * 写入当前时间到设备
     */
    public static void writeCurrentTimeToDevice(ModbusMaster master, int deviceId) {
        try {
            Calendar cal = Calendar.getInstance();
            int year = cal.get(Calendar.YEAR);
            int month = cal.get(Calendar.MONTH) + 1;
            int day = cal.get(Calendar.DATE);
            int hour = cal.get(Calendar.HOUR_OF_DAY);
            int minute = cal.get(Calendar.MINUTE);

            // 写入时间寄存器
            boolean success;
            success = Modbus4jUtils.writeRegister(master, deviceId, 4, (short) year);
            success &= Modbus4jUtils.writeRegister(master, deviceId, 5, (short) month);
            success &= Modbus4jUtils.writeRegister(master, deviceId, 6, (short) day);
            success &= Modbus4jUtils.writeRegister(master, deviceId, 7, (short) hour);
            success &= Modbus4jUtils.writeRegister(master, deviceId, 8, (short) minute);

            if (success) {
                int[] ints = readDeviceRegisters(master, deviceId);
                if(ints[4] == year && ints[5] == month && ints[6] == day && ints[7] == hour && ints[8] == minute) {
                    log.info("设备{}时间写入成功", deviceId);
                } else {
                    log.info("设备{}时间写入失败,板子时间为: year={}, month={}, day={}, hour={}, minute={}",deviceId,ints[4], ints[5], ints[6], ints[7], ints[8]);
                }
                log.info("设备{}时间写入成功", deviceId);
            } else {
                log.info("设备{}时间写入失败", deviceId);
            }
        } catch (Exception e) {
            log.info("设备{}时间写入异常", deviceId, e);
        }
    }

    /**
     * 带重试的设备读取
     */
    public static int[] readDeviceWithRetry(String ip, int port, int deviceId) {
        ModbusMaster master = null;
        int[] lastResult = null; // 用于记录最后一次读取的结果（无论是否满足停止条件）

        try {
            // 只创建一次ModbusMaster，循环内复用
            master = Modbus4jUtils.createModbusMaster(ip, port);

            for (int retry = 0; retry <= CUSTOM_RETRY_TIMES; retry++) {
                try {
                    // 执行读取操作，获取本次结果
                    int[] currentResult = readDeviceRegisters(master, deviceId);
                    // 更新最后一次结果（无论是否满足停止条件，都记录）
                    lastResult = currentResult;

                    // 检查停止条件，如果满足则提前返回（无需等到重试耗尽）
                    if (ModbusResultHandler.createDefaultStopCondition().test(currentResult)) {
                        log.info("设备{}第{}次读取成功（满足条件）: ip={}, port={}",
                                deviceId, retry + 1, ip, port);
                        return currentResult;
                    }

                    // 未满足条件且不是最后一次重试，休眠后继续
                    if (retry < CUSTOM_RETRY_TIMES) {
                        log.info("设备{}第{}次读取未满足条件，准备重试: ip={}, port={}",
                                deviceId, retry + 1, ip, port);
                        Thread.sleep(500);
                    }
                } catch (Exception e) {
                    // 本次读取发生异常，记录日志但不中断重试（继续下一次）
                    log.warn("设备{}第{}次读取发生异常: ip={}, port={}",
                            deviceId, retry + 1, ip, port, e);
                    // 如果是最后一次重试，异常时lastResult可能为null（需后续处理）
                }
            }

            // 循环结束（重试耗尽），此时lastResult为最后一次的结果（可能是正常读取但不满足条件，或null）
            log.info("设备{}重试次数耗尽，返回最后一次结果: ip={}, port={}",
                    deviceId, ip, port);
        } catch (Exception e) {
            // 捕获创建ModbusMaster或休眠时的异常（非读取操作的异常）
            log.error("设备{}连接创建或休眠失败: ip={}, port={}",
                    deviceId, ip, port, e);
            throw new RuntimeException("设备连接或操作异常", e);
        } finally {
            // 无论结果如何，最终销毁连接
            destroyModbusMaster(master, deviceId);
        }

        // 处理最后一次结果可能为null的情况（例如所有重试都异常）
        if (lastResult == null) {
            throw new RuntimeException("设备所有读取尝试均失败（无有效结果）");
        }
        return lastResult;
    }

    public static int writeSelfCheckStatus(ModbusMaster master, int deviceId) {
        // 20251206 写完时间写自检，写自检就在时间后边写就行，不管时间写不写成功
        try {
            Modbus4jUtils.writeRegister(master, deviceId, 15, (short) 1);
            Thread.sleep(1500);
            int[] ints = Modbus4jUtils.readDeviceRegisters(master, deviceId);
            if (ints[15] == 1) {
                return 1;
            } else {
                return 0;
            }
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 读取设备寄存器（线程安全版）
     */
    public static int[] readDeviceRegisters(ModbusMaster master, int deviceId )
            throws ModbusTransportException {

        // 创建读取请求
        ReadHoldingRegistersRequest request = Modbus4jUtils.getReadHoldingRegistersRequest(deviceId,START_ADDRESS, REGISTER_COUNT_36);

        // 发送请求并获取响应
        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;
    }

    public static void writeCurrentTimeUseWriteRegisters(ModbusMaster master, int deviceId) {
        long timestamp = System.currentTimeMillis();
        try {
            Calendar cal = Calendar.getInstance();
            int year = cal.get(Calendar.YEAR);
            int month = cal.get(Calendar.MONTH) + 1;
            int day = cal.get(Calendar.DATE);
            int hour = cal.get(Calendar.HOUR_OF_DAY);
            int minute = cal.get(Calendar.MINUTE);

            writeRegisters(master, deviceId, START_ADDRESS, new short[]{
                    (short) year, (short) month, (short) day, (short) hour, (short) minute
            });
            log.info("设备{}时间写入成功", deviceId);
            long endTime = System.currentTimeMillis();
            log.info("设备{}时间写入耗时: {}ms", deviceId, endTime - timestamp);
        } catch (Exception e) {
            log.error("设备{}时间写入异常", deviceId, e);
        }
    }

    /**
     // 写入并验证年份
     //            allSuccess &= writeAndVerifyField(master, deviceId, 4, (short) year, "年份", year);
     // 写入并验证月份
     //            allSuccess &= writeAndVerifyField(master, deviceId, 5, (short) month, "月份", month);
     //            allSuccess &= writeAndVerifyField(master, deviceId, 6, (short) day, "日期", day);
     // 写入并验证小时
     //            allSuccess &= writeAndVerifyField(master, deviceId, 7, (short) hour, "小时", hour);
     // 写入并验证分钟
     //            allSuccess &= writeAndVerifyField(master, deviceId, 8, (short) minute, "分钟", minute);
     * 增强版：写入并实时验证每个字段
     */
    /**
     * 增强版：写入并实时验证每个字段（增加重试机制）
     */
    public static boolean writeCurrentTimeToDeviceEnhanced(ModbusMaster master, int deviceId, WriteCurrentTimeVo writeCurrentTimeVo) {
        final int MAX_RETRIES = 3;
        int retryCount = 0;

        while (retryCount < MAX_RETRIES) {
            try {
                retryCount++;
                log.info("第{}次尝试设置设备{}时间...", retryCount, deviceId);

                int year = writeCurrentTimeVo.getYear();
                int month = writeCurrentTimeVo.getMonth();
                int day = writeCurrentTimeVo.getDay();
                int hour = writeCurrentTimeVo.getHour();
                int minute = writeCurrentTimeVo.getMinute();

                log.info("开始设置设备{}时间: {}-{}-{} {}:{}", deviceId, year, month, day, hour, minute);

                // 逐个写入并验证
                boolean allSuccess = true;

                // 1. 写入操作（可以使用for循环优化，但保留清晰的结构便于调试）
                allSuccess &= Modbus4jUtils.writeRegister(master, deviceId, 4, (short) year);
                Thread.sleep(2000);

                allSuccess &= Modbus4jUtils.writeRegister(master, deviceId, 5, (short) month);
                Thread.sleep(2000);

                allSuccess &= Modbus4jUtils.writeRegister(master, deviceId, 6, (short) day);
                Thread.sleep(2000);

                allSuccess &= Modbus4jUtils.writeRegister(master, deviceId, 7, (short) hour);
                Thread.sleep(2000);

                allSuccess &= Modbus4jUtils.writeRegister(master, deviceId, 8, (short) minute);
                Thread.sleep(2000);

                if (allSuccess) {
                    int[] ints = readDeviceRegisters(master, deviceId);

                    int deviceYear = ints[4];
                    int deviceMonth = ints[5];
                    int deviceDay = ints[6];
                    int deviceHour = ints[7];
                    int deviceMinute = ints[8];

                    if (isTimeConsistent(deviceYear, deviceMonth, deviceDay, deviceHour, deviceMinute, year, month, day, hour, minute)) {
                        log.info("设备{}时间设置全部完成且验证通过", deviceId);
                        return true;
                    } else {
                        log.info("设备{}时间设置完成，但最终验证失败，准备重试...", deviceId);
                        // 验证失败，等待一下再重试
                        if (retryCount < MAX_RETRIES) {
                            Thread.sleep(3000); // 重试前等待3秒
                            continue;
                        }
                    }
                } else {
                    log.info("设备{}时间设置过程中出现失败，准备重试...", deviceId);
                    // 写入失败，等待一下再重试
                    if (retryCount < MAX_RETRIES) {
                        Thread.sleep(3000); // 重试前等待3秒
                        continue;
                    }
                }

                // 最后一次尝试失败
                if (retryCount == MAX_RETRIES) {
                    log.info("设备{}时间设置失败，已达最大重试次数{}", deviceId, MAX_RETRIES);
                    return false;
                }

            } catch (Exception e) {
                log.info("设备{}时间写入异常，准备重试...", deviceId, e);
                // 异常情况也等待后重试
                if (retryCount < MAX_RETRIES) {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        log.info("重试等待被打断", ie);
                        return false;
                    }
                } else {
                    log.info("设备{}时间写入异常，已达最大重试次数{}", deviceId, MAX_RETRIES, e);
                    return false;
                }
            }
        }

        return false;
    }

    /**
     * 判断设备时间与系统时间是否一致（差异在2分钟内）
     * 考虑跨年、跨月等边界情况
     */
    public static boolean isTimeConsistent(int deviceYear, int deviceMonth, int deviceDay,
                                     int deviceHour, int deviceMinute,
                                     int systemYear, int systemMonth, int systemDay,
                                     int systemHour, int systemMinute) {
        // 1. 首先检查年份差异（考虑跨年）
        if (Math.abs(deviceYear - systemYear) > 1) {
            log.info("年份差异超过1年: 设备{}年 vs 系统{}年", deviceYear, systemYear);
            return false;
        }

        // 2. 转换为总分钟数进行比较（考虑跨年、跨月、跨日）
        long deviceTotalMinutes = calculateTotalMinutes(deviceYear, deviceMonth, deviceDay, deviceHour, deviceMinute);
        long systemTotalMinutes = calculateTotalMinutes(systemYear, systemMonth, systemDay, systemHour, systemMinute);

        long minuteDiff = Math.abs(deviceTotalMinutes - systemTotalMinutes);

        log.info("时间差异比较: 设备分钟数={}, 系统分钟数={}, 差异={}分钟", deviceTotalMinutes, systemTotalMinutes, minuteDiff);

        // 3. 差异在2分钟内认为一致
        return minuteDiff <= 2;
    }
    /**
     * 计算从参考时间点（如2000年）开始的总分钟数
     * 用于准确比较时间差异，考虑跨年跨月
     */
    private static long calculateTotalMinutes(int year, int month, int day, int hour, int minute) {
        // 使用2000-01-01 00:00:00作为参考点
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2000);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        long baseTimeInMillis = cal.getTimeInMillis();

        // 设置目标时间
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1); // 转换为Calendar月份（0-based）
        cal.set(Calendar.DAY_OF_MONTH, day);
        cal.set(Calendar.HOUR_OF_DAY, hour);
        cal.set(Calendar.MINUTE, minute);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        long targetTimeInMillis = cal.getTimeInMillis();

        // 返回总分钟数
        return (targetTimeInMillis - baseTimeInMillis) / (1000 * 60);
    }
    /**
     * 写入并验证单个字段（带重试机制）
     */
    private static boolean writeAndVerifyField(ModbusMaster master, int deviceId,
                                               int address, short value,
                                               String fieldName, int expectedValue) {
        // 最大重试次数
        int maxRetry = 3;

        // 进行最多3次尝试
        for (int retry = 1; retry <= maxRetry; retry++) {
            try {
                log.info("第{}次尝试写入{}: {} (地址={})",
                        retry, fieldName, expectedValue, address);

                // 1. 写入操作
                boolean writeSuccess = Modbus4jUtils.writeRegister(master, deviceId, address, value);

                if (!writeSuccess) {
                    log.warn("第{}次写入{}失败", retry, fieldName);
                    continue; // 继续下一次重试
                }

                // 3. 读取验证
//                int[] ints = readDeviceRegisters(master, deviceId);
//                if (ints.length > address && ints[address] == expectedValue) {
//                    log.info("{}验证通过，写入值: {}", fieldName, expectedValue);
//                    return true; // 成功，直接返回
//                } else {
//                    int actualValue = ints.length > address ? ints[address] : -1;
//                    log.warn("第{}次{}验证失败，期望: {}，实际: {}",
//                            retry, fieldName, expectedValue, actualValue);
//                    // 验证失败，继续下一次重试
//                }

            } catch (Exception e) {
                log.error("第{}次写入{}时发生异常", retry, fieldName, e);
            }

            // 如果不是最后一次重试，可以稍作等待再尝试（可选）
            if (retry < maxRetry) {
                try {
                    Thread.sleep(500); // 重试间隔
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }

        // 所有重试都失败了
        log.error("{}写入失败，已达到最大重试次数{}", fieldName, maxRetry);
        return false;
    }

    /**
     * 验证时间寄存器
     */
    private static boolean verifyTimeRegisters(int[] registers,
                                               int year, int month, int day,
                                               int hour, int minute) {
        if (registers.length < 9) {
            log.warn("寄存器数量不足，无法验证时间");
            return false;
        }

        boolean verified = true;

        if (registers[4] != year) {
            log.warn("年份不匹配: 期望={}, 实际={}", year, registers[4]);
            verified = false;
        }

        if (registers[5] != month) {
            log.warn("月份不匹配: 期望={}, 实际={}", month, registers[5]);
            verified = false;
        }

        if (registers[6] != day) {
            log.warn("日期不匹配: 期望={}, 实际={}", day, registers[6]);
            verified = false;
        }

        if (registers[7] != hour) {
            log.warn("小时不匹配: 期望={}, 实际={}", hour, registers[7]);
            verified = false;
        }

        if (registers[8] != minute) {
            log.warn("分钟不匹配: 期望={}, 实际={}", minute, registers[8]);
            verified = false;
        }

        return verified;
    }
}
