package org.gyk.modules.modbus.utils;

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.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import jssc.SerialPort;
import lombok.extern.slf4j.Slf4j;
import org.gyk.modules.modbus.serialPort.SerialPortWrapperImpl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;


/**
 * modbus-tcp的工具类
 *
 * jiang，20231012
 */
@Slf4j
public class modBusUtils {

    /**
     * 工厂。
     */
    static ModbusFactory modbusFactory;

    static {
        if (modbusFactory == null) {
            modbusFactory = new ModbusFactory();
        }
    }

    /**
     * 获取master
     *
     * @throws ModbusInitException
     */
    public static ModbusMaster getMaster(String host, int port) {

        IpParameters params = new IpParameters();
        params.setHost(host);
        params.setPort(port);
        // modbusFactory.createRtuMaster(wapper); //RTU 协议
        // modbusFactory.createUdpMaster(params);//UDP 协议
        // modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
        ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协议
        try {
            master.init();
        } catch (ModbusInitException e) {
            e.printStackTrace();
        }


        return master;
    }

    public static ModbusMaster getRtuIpMaster(String host, int port)  {
        IpParameters params = new IpParameters();
        params.setHost(host);
        params.setPort(port);
        params.setEncapsulated(true);
        ModbusMaster master = modbusFactory.createTcpMaster(params, false);
        try {
            //设置超时时间
            master.setTimeout(1000);
            //设置重连次数
            master.setRetries(3);
            //初始化
            master.init();
        } catch (ModbusInitException e) {
            e.printStackTrace();
        }
        return master;
    }

    /**
     * RTU
     * @return
     * @throws ModbusInitException
     */
    public static ModbusMaster getSerialPortRtuMaster(){
        // 设置串口参数，串口是COM1，波特率是9600
         SerialPortWrapperImpl wrapper = new SerialPortWrapperImpl("COM2", 9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, 0, 0);
//        SerialPortWrapperImpl wrapper = new SerialPortWrapperImpl(portName, baudRate,
//                dataBits, stopBits, parity, 0, 0);
        ModbusMaster master = modbusFactory.createRtuMaster(wrapper);
        try {
            //设置超时时间
            master.setTimeout(1000);
            //设置重连次数
            master.setRetries(3);
            //初始化
            master.init();
        } catch (ModbusInitException e) {
            log.error("串口连接异常~");
            e.printStackTrace();
        }
        return master;
    }
    /**
     *
     * @param portName 串口名
     * @param baudRate 波特率
     * @param dataBits 数据位
     * @param stopBits 中止位
     * @param parity   校验位
     * @return
     * @throws ModbusInitException
     */
    public static ModbusMaster getSerialPortAsciiMaster(String portName, Integer baudRate, Integer dataBits,
                                                        Integer stopBits, Integer parity){
        // 设置串口参数，串口是COM1，波特率是9600
        // SerialPortWrapperImpl wrapper = new SerialPortWrapperImpl("COM2", 9600,SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, 0, 0);
        SerialPortWrapperImpl wrapper = new SerialPortWrapperImpl(portName, baudRate,
                dataBits, stopBits, parity, 0, 0);
        ModbusMaster master = modbusFactory.createAsciiMaster(wrapper);
        try {
            //设置超时时间
            master.setTimeout(1000);
            //设置重连次数
            master.setRetries(3);
            //初始化
            master.init();
        } catch (ModbusInitException e) {
            log.error("串口连接异常~");
            e.printStackTrace();
        }
        return master;
    }

    /**
     * 读取[01 Coil Status 0x]类型 开关数据
     *
     * @param slaveId
     *            slaveId 从地址 （仪表地址）
     * @param offset
     *            位置
     * @return 读取值
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     */
    public static Boolean readCoilStatus(int slaveId, int offset,String ip ,int prot)
            throws ModbusTransportException, ErrorResponseException {
        // 01 Coil Status
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
        Boolean value = getMaster(ip, prot).getValue(loc);
        return value;
    }

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



    /**
     * 读取[03 Holding Register类型 4x]模拟量数据
     *
     * @param slaveId
     *            slave Id
     * @param offset
     *            位置
     * @param dataType
     *            数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     */
    public static Number readHoldingRegister(int slaveId, int offset, int dataType,String ip ,int prot )
            throws ModbusTransportException, ErrorResponseException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        Number value = getMaster(ip,prot).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
     *             异常
     */
    public static Number readInputRegisters(int slaveId, int offset, int dataType ,String ip ,int prot)
            throws ModbusTransportException, ErrorResponseException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        Number value = getMaster(ip,prot).getValue(loc);
        return value;
    }

    /**
     * 批量读取使用方法
     *
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     */
    public static void batchRead(String ip ,int prot ) throws ModbusTransportException, ErrorResponseException {

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

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

        ModbusMaster master = getMaster(ip,prot);

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

//    /**
//     * 测试
//     *
//     * @param args
//     */
//    public static void main(String[] args) {
//        try {
//            // 01测试
//            Boolean v011 = readCoilStatus(1, 0);
//            Boolean v012 = readCoilStatus(1, 1);
//            Boolean v013 = readCoilStatus(1, 6);
//            System.out.println("v011:" + v011);
//            System.out.println("v012:" + v012);
//            System.out.println("v013:" + v013);
//            // 02测试
//            Boolean v021 = readInputStatus(1, 0);
//            Boolean v022 = readInputStatus(1, 1);
//            Boolean v023 = readInputStatus(1, 2);
//            System.out.println("v021:" + v021);
//            System.out.println("v022:" + v022);
//            System.out.println("v023:" + v023);
//
//            // 03测试
//            Number v031 = readHoldingRegister(1, 1, DataType.FOUR_BYTE_FLOAT);// 注意,float
//            Number v032 = readHoldingRegister(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上
//            System.out.println("v031:" + v031);
//            System.out.println("v032:" + v032);
//
//            // 04测试
//            Number v041 = readInputRegisters(1, 1, DataType.FOUR_BYTE_FLOAT);//
//            Number v042 = readInputRegisters(1, 3, DataType.FOUR_BYTE_FLOAT);//
//            System.out.println("v041:" + v041);
//            System.out.println("v042:" + v042);
//            // 批量读取
//            batchRead();
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * 写 [01 Coil Status(0x)]写一个 function ID = 5
     *
     * @param slaveId
     *            slave的ID
     * @param writeOffset
     *            位置
     * @param writeValue
     *            值
     * @return 是否写入成功
     * @throws ModbusTransportException
     */
    public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue ,String ip ,int prot)
            throws ModbusTransportException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(ip, prot);
        // 创建请求
        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 = 15
     *
     * @param slaveId
     *            slaveId
     * @param startOffset
     *            开始位置
     * @param bdata
     *            写入的数据
     * @return 是否写入成功
     * @throws ModbusTransportException
     */
    public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata,String ip ,int prot)
            throws ModbusTransportException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(ip, prot);
        // 创建请求
        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
        if (response.isException()) {
            return false;
        } else {
            return true;
        }

    }

    /***
     * 写[03 Holding Register(4x)] 写一个 function ID = 6
     *
     * @param slaveId  11
     * @param writeOffset
     * @param writeValue
     * @return
     * @throws ModbusTransportException
     */
    public static boolean writeRegister(int slaveId, int writeOffset, int writeValue, String ip ,int prot)
            throws ModbusTransportException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(ip, prot);
        // 创建请求对象
        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
        WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
        System.out.println("我这都是什么值："+response);
        if (response.isException()) {
            System.out.println("我错哪了？"+response.getExceptionMessage());
//            log.error(response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }

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

    /**
     * rtu写数据
     * @param slaveId 从站ID
     * @param writeOffset 偏移
     * @param writeValue  写入值
     * @return
     * @throws ModbusTransportException
     */
    public static boolean writeRegisterRtu(int slaveId, int writeOffset, int writeValue)
            throws ModbusTransportException {
        // 获取master
        ModbusMaster rtuMaster = getSerialPortRtuMaster();
        // 创建请求对象
        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
        WriteRegisterResponse response = (WriteRegisterResponse) rtuMaster.send(request);
        if (response.isException()) {
//            log.error(response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }

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

    }

//    public static void main(String[] args) {
//        try {
            //@formatter:off
            // 测试01
//			boolean t01 = writeCoil(1, 0, true);
//			System.out.println("T01:" + t01);

            // 测试02
//			boolean t02 = writeCoils(1, 0, new boolean[] { true, false, true });
//			System.out.println("T02:" + t02);

            // 测试03
//			short v = -3;
//			boolean t03 = writeRegister(1, 0, v);
//			System.out.println("T03:" + t03);
            // 测试04
//			boolean t04 = writeRegisters(1, 0, new short[] { -3, 3, 9 });
//			System.out.println("t04:" + t04);
            //写模拟量
//            writeHoldingRegister(1,0, 10.1f, DataType.FOUR_BYTE_FLOAT);

            //@formatter:on
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//
//    }

    /**
     *short数组转byte
     * @param data
     * @return
     */
    public static byte [] shorts2Bytes(short [] data){
        byte[] byteValue = new byte[data.length * 2];
        for (int i = 0; i < data.length; i++) {
            byteValue[i * 2] = (byte) (data[i] & 0xff);
            byteValue[i * 2 + 1] = (byte) ((data[i] & 0xff00) >> 8);
        }
        return byteValue;
    }
    /**
     * Byte数组转short数组
     *
     * @param bytes
     * @return
     */
    public static short[] bytesToShort(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        short[] shorts = new short[bytes.length / 2];
        ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
        return shorts;

    }

    //大端转换小端
    public static float littleEndianSwap(float big) {

        int fbit = Float.floatToIntBits(big);
        int a = fbit << 16;
        a &= 0xFFFF0000;
        System.out.println("a:"+ a);

        int b = fbit >> 16;
        b &= 0xFFFF;
        System.out.println("b:"+ b);

        int l;
        l = a|b;
        System.out.println("l:"+l);

        float little = Float.intBitsToFloat(l);
        return little;
    }

    //方法1：
//(3412) 小端交换字节模式
    public static float big2Little(float big){
        // 把float转换为byte[]
        int fbit = Float.floatToIntBits(big);
        System.out.println("+========1======="+fbit);
        byte[] b = new byte[4];
        b[0] = (byte) (fbit >> 16);
        b[1] = (byte) (fbit);

        int l;
        l = b[0];
        l &= 0xff;
        l |= ((long) b[2] << 16);
        float little = Float.intBitsToFloat(l);
        return little;
    }

    //方法2：
    public static float big2Little2(float big){

        // 把float转换为byte[]
        int fbit = Float.floatToIntBits(big);
        System.out.println("+=======2========"+fbit);
        int a = fbit << 16;
        a &= 0xFFFF0000;
        System.out.println("a:"+ a);

        int b = fbit >> 16;

        b &= 0xFFFF;
        System.out.println("b:"+ b);

        int l;
        l = a|b;

        System.out.println("l:"+l);

        float little = Float.intBitsToFloat(l);
        return little;
    }

    //大端（1234）转小端（4321）
    public static float big2Little3(float big){
        // 把float转换为byte[]
        int fbit = Float.floatToIntBits(big);
        System.out.println("+========1======="+Integer.toBinaryString(fbit));

        byte[] b = new byte[4];
        b[0] = (byte) (fbit >> 24);
        b[1] = (byte) (fbit >> 16);
        b[2] = (byte) (fbit >> 8);
        b[3] = (byte) (fbit);
        //以下换位置没有看懂，jiang

        int l;
        l = b[0];
        System.out.println("+========6======="+Integer.toBinaryString(l));
        l &= 0xff;
        System.out.println("--------1-------"+l);
        l |= ((long) b[1] << 8);
        System.out.println("--------2-------"+(long) b[1]);
        System.out.println("+========8======="+Integer.toBinaryString(l));
        l &= 0xffff;
        System.out.println("--------3-------"+l);
        System.out.println("+========9======="+Integer.toBinaryString(l));


        l |= ((long) b[2] << 16);
        System.out.println("--------4-------"+(long) b[2]);
        System.out.println("+========10======="+Integer.toBinaryString(l));
        l &= 0xffffff;
        System.out.println("--------5-------"+l);
        System.out.println("+========11======="+Integer.toBinaryString(l));
        l |= ((long) b[3] << 24);
        System.out.println("--------6-------"+(long) b[3]);
        System.out.println("+========12======="+Integer.toBinaryString(l));
        float little = Float.intBitsToFloat(l);
        return little;
    }

    /**
     * 大端（1234）转大端交换（2143）
     * 把咱们能看懂的数字转成相对应的模式
     * 在modbuspoll上咱们能看懂的数字是大端交换模式。
     * 该方法按照modbuspoll上的解释应该是 大端交换模式转大端模式
     * @param big
     * @return
     */
    public static float big2bigsw(float big){

        // 把float转换为byte[]
        int fbit = Float.floatToIntBits(big);
        byte[] b = new byte[4];
        b[0] = (byte) (fbit >> 24);
        b[1] = (byte) (fbit >> 16);
        b[2] = (byte) (fbit >> 8);
        b[3] = (byte) (fbit);
//        byte[] c = new byte[4];
//        c[0] = b[1];
//        c[1] = b[0];
//        c[2] = b[3];
//        c[3] = b[2];

        //把b[1]放到b[0]前面
        int f;
        f = b[2]; //赋予初始值b[2]
        f &= 0xff; //保留最后8位二进制，即保留b[2]使其置于最后
        f |= ((long) b[3] << 8); //把b[3]向左位移8位即在b[2]之前
        f &= 0xffff;//保留最后16位二进制，即按b[3]b[2]的顺序存储
        f|=((long)b[0]<<16);//把b[0]向左位移16位即在b[3]之前
        f &= 0xffffff;//保留最后24位,即按b[0]b[3]b[2]的顺序存储
        f|=((long)b[1]<<24);//b[1]向左位移24位即在b[0]之前
        f &= 0xffffffff;//保留最后32位，即按b[1]b[0]b[3]b[2]的顺序存储
        float floatValue = Float.intBitsToFloat(f);
        System.out.println("----1--"+floatValue);
        return floatValue;
    }

    public static float toLittleEndian(float value) {
        ByteBuffer buffer = ByteBuffer.allocate(4); // Allocate 4 bytes for a float
        buffer.order(ByteOrder.BIG_ENDIAN); // Set the byte order to big-endian
        buffer.putFloat(value); // Put the float value into the buffer

        ByteBuffer buffer2 = ByteBuffer.allocate(4); // Allocate 4 bytes for a float
        buffer2.order(ByteOrder.LITTLE_ENDIAN); // Set the byte order to little-endian
        buffer2.putInt(buffer.getInt()); // Get the big-endian integer value and put it into the little-endian buffer

        return buffer2.getFloat(); // Get the float value from the little-endian buffer
    }

    public static float toBigEndian(float value) {
        ByteBuffer buffer = ByteBuffer.allocate(4); // Allocate 4 bytes for a float
        buffer.order(ByteOrder.LITTLE_ENDIAN); // Set the byte order to little-endian
        buffer.putFloat(value); // Put the float value into the buffer

        ByteBuffer buffer2 = ByteBuffer.allocate(4); // Allocate 4 bytes for a float
        buffer2.order(ByteOrder.BIG_ENDIAN); // Set the byte order to big-endian
        buffer2.putInt(buffer.getInt()); // Get the little-endian integer value and put it into the big-endian buffer

        return buffer2.getFloat(); // Get the float value from the big-endian buffer
    }

    public static float toBigEndianSwapped(float value) {
        ByteBuffer buffer = ByteBuffer.allocate(4); // Allocate 4 bytes for a float
        buffer.order(ByteOrder.BIG_ENDIAN); // Set the byte order to big-endian
        buffer.putFloat(value); // Put the float value into the buffer

        ByteBuffer buffer2 = ByteBuffer.allocate(4); // Allocate 4 bytes for a float
        buffer2.order(ByteOrder.BIG_ENDIAN); // Set the byte order to big-endian
        buffer2.putFloat(buffer.getFloat()); // Get the big-endian integer value and put it into the big-endian buffer
        System.out.println("----2--"+buffer2.getFloat());
        return buffer2.getFloat(); // Get the float value from the big-endian buffer
    }
}



