package com.xxxxtech.srac.modules.relay;

import com.xxxxtech.srac.commom.Constants;
import com.xxxxtech.srac.utils.StringUtils;
import net.wimpi.modbus.ModbusException;
import net.wimpi.modbus.ModbusIOException;
import net.wimpi.modbus.ModbusSlaveException;
import net.wimpi.modbus.io.ModbusTCPTransaction;
import net.wimpi.modbus.msg.*;
import net.wimpi.modbus.net.TCPMasterConnection;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

public class ModbusUtil {

    /**
     * 功能描述：获取寄存器状态，0为未工作，1为已工作。
     * <br>创建时间：2020/2/20 12:03 <br>
     *
     * @param ip    寄存器IP地址
     * @param count 初始次数
     * @author <a href="mailto:dawson.ao@xxxxtech.com">Dawson AO</a>
     */
    public int readDigitalStatus(String ip, int count) {
        int data = 0;
        try {
            InetAddress addr = InetAddress.getByName(ip);

            TCPMasterConnection con = new TCPMasterConnection(addr);
            con.setPort(Constants.RELAY_PORT);
            con.connect();
            con.close();
            data = 1;
        } catch (Exception ex) {
//            ex.printStackTrace();
            count = count + 1;
        }
        if (count <= 2 && count != 0) {
            readDigitalStatus(ip, count);
        }
        return data;
    }

    /**
     * 查询Function 为Input Status的寄存器
     *
     * @param ip
     * @param address
     * @return
     * @throws ModbusIOException
     * @throws ModbusSlaveException
     * @throws ModbusException
     */
    /**
     * 功能描述：查询功能为Input Status的寄存器
     * <br>创建时间：2020/2/20 11:41 <br>
     *
     * @param ip      寄存器的IP地址
     * @param address 寄存器的端口地址
     * @author <a href="mailto:dawson.ao@xxxxtech.com">Dawson AO</a>
     */
    public int readDigitalInput(String ip, int address) {
        int data = 0;

        try {
            InetAddress addr = InetAddress.getByName(ip);

            // 建立连接
            TCPMasterConnection con = new TCPMasterConnection(addr);

            con.setPort(Constants.RELAY_PORT);

            con.connect();

            // 因为云端系统配置是以1开头，但是继电器是0开始，所以需要减一才能一致
            address = address - 1;

            // 第一个参数是寄存器的地址，第二个参数时读取多少个
            ReadInputDiscretesRequest req = new ReadInputDiscretesRequest(address, 1);

            // 这里设置的Slave Id, 读取的时候这个很重要
            req.setUnitID(Constants.SLAVE_ID);

            ModbusTCPTransaction trans = new ModbusTCPTransaction(con);

            trans.setRequest(req);

            // 执行查询
            trans.execute();

            // 得到结果
            ReadInputDiscretesResponse res = (ReadInputDiscretesResponse) trans.getResponse();

            if (res.getDiscretes().getBit(0)) {
                data = 1;
            }

            // 关闭连接
            con.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return data;
    }

    public static int readInputRegister(String ip, int address, int len) {
        int data = 0;

        try {
            InetAddress addr = InetAddress.getByName(ip);
            TCPMasterConnection con = new TCPMasterConnection(addr);

            //Modbus.DEFAULT_PORT;
            con.setPort(Constants.RELAY_PORT);
            con.connect();

            // 因为云端系统配置是以1开头，但是继电器是0开始，所以需要减一才能一致
            address = address - 1;

            //这里重点说明下，这个地址和数量一定要对应起来
            ReadInputRegistersRequest req = new ReadInputRegistersRequest(address, 8);

            //这个SlaveId一定要正确
            req.setUnitID(Constants.SLAVE_ID);

            ModbusTCPTransaction trans = new ModbusTCPTransaction(con);

            trans.setRequest(req);

            trans.execute();

            ReadInputRegistersResponse res = (ReadInputRegistersResponse) trans.getResponse();

            data = res.getRegisterValue(0);

            con.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return data;
    }

    public int readInputRegister(String ip, int address) {
        int data = 0;

        try {
            InetAddress addr = InetAddress.getByName(ip);
            TCPMasterConnection con = new TCPMasterConnection(addr);

            //Modbus.DEFAULT_PORT;
            con.setPort(Constants.RELAY_PORT);
            con.connect();

            // 因为云端系统配置是以1开头，但是继电器是0开始，所以需要减一才能一致
            address = address - 1;

            //这里重点说明下，这个地址和数量一定要对应起来
            ReadInputRegistersRequest req = new ReadInputRegistersRequest(address, 8);

            //这个SlaveId一定要正确
            req.setUnitID(Constants.SLAVE_ID);

            ModbusTCPTransaction trans = new ModbusTCPTransaction(con);

            trans.setRequest(req);

            trans.execute();

            ReadInputRegistersResponse res = (ReadInputRegistersResponse) trans.getResponse();

            data = res.getRegisterValue(0);

            con.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return data;
    }

    /**
     * 功能描述：
     * <br>创建时间：2020/2/20 11:40 <br>
     *
     * @param address 控制器端口地址
     * @param ip      控制器IP地址
     * @author <a href="mailto:dawson.ao@xxxxtech.com">Dawson AO</a>
     */
    public int readDigitalOutput(String ip, int address) {
        int data = 0;
        try {
            InetAddress addr = InetAddress.getByName(ip);

            TCPMasterConnection con = new TCPMasterConnection(addr);
            con.setPort(Constants.RELAY_PORT);
            con.connect();

            // 因为云端系统配置是以1开头，但是继电器是0开始，所以需要减一才能一致
            address = address - 1;

            ReadCoilsRequest req = new ReadCoilsRequest(address, 1);
            System.out.println(" >>>>>>>>>>>>>>>>>> " + req);
            req.setUnitID(Constants.SLAVE_ID);

            ModbusTCPTransaction trans = new ModbusTCPTransaction(con);

            trans.setRequest(req);

            trans.execute();

            ReadCoilsResponse res = ((ReadCoilsResponse) trans.getResponse());

            if (res.getCoils().getBit(0)) {
                data = 1;
            }

            con.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return data;
    }

    public int readRegister(String ip, int address) {
        int data = 0;
        try {
            InetAddress addr = InetAddress.getByName(ip);

            TCPMasterConnection con = new TCPMasterConnection(addr);

            con.setPort(Constants.RELAY_PORT);
            con.connect();

            // 因为云端系统配置是以1开头，但是继电器是0开始，所以需要减一才能一致
            address = address - 1;
            ReadMultipleRegistersRequest req = new ReadMultipleRegistersRequest(address, 1);
            req.setUnitID(Constants.SLAVE_ID);

            ModbusTCPTransaction trans = new ModbusTCPTransaction(con);

            trans.setRequest(req);

            trans.execute();

            ReadMultipleRegistersResponse res = (ReadMultipleRegistersResponse) trans.getResponse();

            data = res.getRegisterValue(0);

            con.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return data;
    }

    /**
     * 写入数据到真机，数据类型是RE
     *
     * @param ip
     * @param address
     * @param value
     */
    public static void writeRegister(String ip, int address, int value) {

        try {
            InetAddress addr = InetAddress.getByName(ip);

            TCPMasterConnection connection = new TCPMasterConnection(addr);
            connection.setPort(Constants.RELAY_PORT);
            connection.connect();

            ModbusTCPTransaction trans = new ModbusTCPTransaction(connection);

			/*UnityRegister register = new UnityRegister(value);

			WriteSingleRegisterRequest req = new WriteSingleRegisterRequest(
					address, register);

			req.setUnitID(Constants.SLAVE_ID);
			trans.setRequest(req);

			System.out.println("ModbusSlave: FC" + req.getFunctionCode()
					+ " ref=" + req.getReference() + " value="
					+ register.getValue());*/
            trans.execute();

            connection.close();
        } catch (Exception ex) {
            System.out.println("Error in code");
            ex.printStackTrace();
        }
    }

    /**
     * 灯光控制，当前使用
     *
     * @param ip 继电器组IP地址
     */
    public static void writeDigitalOutput1(String ip,
                                           int address, int value) {
        Socket lightClient = new Socket();
        String message = "";
        if (lightClient.isConnected()) {
            try {
                lightClient.close();
            } catch (IOException e) {
                return;
            }
        }
        while (true) {
            try {
                //  准备开灯
                System.out.println("正在连接......" + ip);
                lightClient.connect(new InetSocketAddress(ip, 10000), 4000);
                OutputStream sendMsg = lightClient.getOutputStream();
                InputStream getMsg = lightClient.getInputStream();
                boolean isOpenRelay = value == Constants.RELAY_CLOSE;
                //  不能大于32
                if (isOpenRelay) {
                    message = Constants.open_instruction[address - 1];
                } else {
                    message = Constants.close_instruction[address - 1];
                }
                byte[] bytes = StringUtils.hexStrToBinaryStr(message);
                sendMsg.write(bytes);
                sendMsg.flush();
                String gmsg = StringUtils.bytesToHex(getMsg).toUpperCase();
                System.out.println(" >> " + gmsg);
                sendMsg.close();
                lightClient.close();
                System.out.println("关闭连接");

                break;
            } catch (Exception e2) {
                e2.printStackTrace();
                if (lightClient != null) {
                    try {
                        if (lightClient.isConnected()) {
                            lightClient.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                //TODO: 这里应该要提醒店家没有打开继电器
                break;
            } finally {
                if (lightClient != null) {
                    try {
                        if (lightClient.isConnected()) {
                            lightClient.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            }
        }
    }

    /**
     * 功能描述：读取寄存器状态信息
     * <br>创建时间：2020/2/20 11:58 <br>
     *
     * @author <a href="mailto:dawson.ao@xxxxtech.com">Dawson AO</a>
     */
    public void readAllDigitalStatus(String... ips) {
        if (ips.length <= 0) {
            return;
        }
        // 遍历所有iP
        for (String ip : ips) {

        }
    }

    /**
     * 写入数据到真机的DO类型的寄存器上面
     *
     * @param ip      继电器IP地址
     * @param address 需要控制的位，从0开始，16位就是0-15
     * @param value   是否开灯或关灯，0为关，1为开
     */
//    @Async
    public void writeDigitalOutput(String ip,
                                   int address, int value) {
        innerWriteDigitalOutput(ip, address, value, 0);
        int status = readDigitalOutput(ip, address);
        System.out.println(" >>>>>>> ** The control status is: "+ status);
    }

    /**
     * 功能描述：写入数据到真机的DO类型的寄存器上面
     * <br>创建时间：2020/2/20 11:10 <br>
     *
     * @param ip      继电器IP地址
     * @param address 需要控制的位，从0开始，16位就是0-15
     * @param value   是否开灯或关灯，0为关，1为开
     * @author <a href="mailto:dawson.ao@xxxxtech.com">Dawson AO</a>
     */
    public void innerWriteDigitalOutput(String ip,
                                        int address, int value, int count) {
        System.out.println("操作继电器： " + ip + " - " + address);
        try {
            InetAddress addr = InetAddress.getByName(ip);

            TCPMasterConnection connection = new TCPMasterConnection(addr);
            connection.setTimeout(1000);
            connection.setPort(Constants.RELAY_PORT);
            connection.connect();

            ModbusTCPTransaction trans = new ModbusTCPTransaction(connection);

            boolean val = true;

            if (value == 0) {
                val = false;
            }

			System.out.println(" --------- oadr: "+ address);
            // 因为云端系统配置是以1开头，但是继电器是0开始，所以需要减一才能一致
            address = address - 1;
			System.out.println(" --------- nadr: "+ address +" --- status: " + val);

            WriteCoilRequest req = new WriteCoilRequest(address, val);

            req.setUnitID(Constants.SLAVE_ID);
            trans.setRequest(req);

            System.out.println(" -------------- req : " + req);

            trans.execute();
            connection.close();

            count = 0;
        } catch (Exception ex) {
            System.out.println("writeDigitalOutput Error in code");
            ex.printStackTrace();
            count = count + 1;
        }
        // 如果第一次无法后去到设备连接，尝试进行2次连接。
        if (count <= 3 && count != 0) {
            try {
                Thread.sleep(2 * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            innerWriteDigitalOutput(ip, address, value, count);
        }
    }

    public static void main(String[] args) {
        ModbusUtil modbusUtil = new ModbusUtil();
        modbusUtil.writeDigitalOutput("192.168.1.232",  1, 0);
    }
}
