package com.zehong.web.controller.equipment;

import com.zehong.common.core.domain.AjaxResult;
import com.zehong.common.utils.TCP.TCPClient;
import com.zehong.quartz.config.QuartzTaskMonitor;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TEquipmentInfo;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITEquipmentInfoService;
import com.zehong.system.service.ITStoreyInfoService;
import com.zehong.system.task.DeviceTaskScheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;

/**
 * 生产设备数据采集
 *
 * @author zehong
 * @date 2025-05-28
 */
@RestController
@RequestMapping("/equipmentData")
public class EquipmentDataCollection {
    private static final Logger log = LoggerFactory.getLogger(EquipmentDataCollection.class);
    @Autowired
    private ITEquipmentInfoService tEquipmentInfoService;
    @Autowired
    private ITStoreyInfoService tStoreyInfoService;
    @Autowired
    private ITEquipmentAlarmDataService tEquipmentAlarmDataService;

    @Autowired
    private DeviceTaskScheduler deviceTaskScheduler;

    // 新增：注入监控器
    @Autowired
    private QuartzTaskMonitor quartzTaskMonitor;
    /**
     * 触发设备监控任务
     * @return 执行结果
     */
    @GetMapping("/trigger/{ipAndPort}")
    public AjaxResult triggerDeviceTask(@PathVariable("ipAndPort") String ipAndPort) {
        long storeyId = 0;
        try {
            String[] split = ipAndPort.split(",");
            String storeyIdStr = split[0];
            String DEFAULT_TEST_IP = split[1];
            int port =Integer.parseInt(split[2]);


            TStoreyInfo tStoreyInfo = tStoreyInfoService.selectTStoreyInfoByCode(storeyIdStr);
            storeyId = tStoreyInfo.getfStoreyId();
            log.info("开始执行测试设备任务：storeyId={}, ip={}, port={}",
                    storeyId, DEFAULT_TEST_IP, port);

            // 调用调度器执行任务
            deviceTaskScheduler.scheduleDeviceMonitoring(storeyId, DEFAULT_TEST_IP, port);

            // 新增：立即监控任务状态
            quartzTaskMonitor.monitorTaskStatus(storeyId);

            log.info("测试设备任务已提交：storeyId={}", storeyId);
            return AjaxResult.success("设备任务已成功提交",
                    String.format("任务参数：storeyId=%d, ip=%s, port=%d",
                            storeyId, DEFAULT_TEST_IP, port));
        } catch (Exception e) {
            log.error("触发设备任务失败：storeyId={}", storeyId, e);
            return AjaxResult.error("触发设备任务失败：" + e.getMessage());
        }
    }
    /**
     * 1.老化柜巡查
     * 2.老化层断电
     */
    @GetMapping("/equipmentPatrol")
    public void equipmentPatrol() {
        TEquipmentInfo equipmentInfo = new TEquipmentInfo();
        equipmentInfo.setfEquipmentType("1");
        List<TEquipmentInfo> equipmentInfos = tEquipmentInfoService.selectTEquipmentInfoList(equipmentInfo);
        try {
            if (equipmentInfos.size() == 0) {
                log.error("老化柜列表查询结果为空");
                throw new Exception("无老化柜信息！");
            }

            /* 1.老化柜通讯 */
            String sendMsg1 = "00010000000601040000000A";
            String receiveMsg1 = null;
            TEquipmentAlarmData alarmData = new TEquipmentAlarmData();
            for (TEquipmentInfo info : equipmentInfos) {
                receiveMsg1 = agingCabinetCommunication(info, sendMsg1);
                if (receiveMsg1 == null || receiveMsg1.length() != 58) {
                    // 记录异常数据
                    alarmData.setfAlarmType("01"); //01.老化柜 02.机械臂 03.老化层 04.点位
                    alarmData.setfEquipmentCode(info.getfEquipmentCode());
                    alarmData.setfAlarmData("老化柜通讯失败");
                    tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
                    // 更新老化柜状态（“0”空闲，“1”运行，“2”故障）
                    info.setfStatus("2");
                    tEquipmentInfoService.updateTEquipmentInfo(info);
                } else {
                    // 解析服务器返回信息
                    // 示例：00 01 00 00 00 17 01 04 14 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                    String flag = receiveMsg1.substring(18, 58);
                    if ("0000000000000000000000000000000000000000".equals(flag) && !"0".equals(info.getfStatus())) {
                        // 更新老化柜状态（“0”空闲，“1”运行，“2”故障）
                        info.setfStatus("0");
                        info.setfUpdateTime(new Date());
                        tEquipmentInfoService.updateTEquipmentInfo(info);
                    } else if (!"0000000000000000000000000000000000000000".equals(flag) && "0".equals(info.getfStatus())) {
                        info.setfStatus("1");
                        info.setfUpdateTime(new Date());
                        tEquipmentInfoService.updateTEquipmentInfo(info);
                    } else if ("0000000000000000000000000000000000000000".equals(flag)) {
                        // 解析层状态，根据层编号修改状态
                        int startIndex;
                        int endIndex;
                        int code; //组成老化层编号
                        String status;
                        TStoreyInfo storeyInfo = new TStoreyInfo();
                        String storeyCode;
                        for (startIndex = 18, endIndex = 22, code = 1; endIndex <= receiveMsg1.length(); startIndex += 4, endIndex += 4, code++) {
                            status = receiveMsg1.substring(startIndex, endIndex);
                            storeyCode = info.getfEquipmentCode() + "-" + code;
                            storeyInfo = tStoreyInfoService.selectTStoreyInfoByCode(storeyCode);
                            if (storeyInfo == null) {
                                // 记录异常数据
                                alarmData.setfAlarmType("01"); //01.老化柜 02.机械臂 03.老化层 04.点位
                                alarmData.setfEquipmentCode(info.getfEquipmentCode());
                                alarmData.setfAlarmData("下属" + storeyCode + "号老化层不存在");
                                tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
                                continue;
                            }
                            if ("0000".equals(status)) {
                                /* 2.老化层断电 */
                                // 示例：000100000006 01 功能码05 寄存器地址0000 断电0000
                                String addr = Integer.toHexString(code - 1).length() < 2 ? "0" + Integer.toHexString(code - 1) : Integer.toHexString(code - 1);
                                String sendMsg2 = "000100000006010500" + addr + "0000";
                                String receiveMsg2 = storeyCommunication(storeyInfo, sendMsg2);
                                if (sendMsg2.equals(receiveMsg2)) {
                                    // 更新老化层状态
                                    storeyInfo.setfStatus("3"); //断电
                                    tStoreyInfoService.updateTStoreyInfo(storeyInfo);
                                } else {
                                    // 记录异常数据
                                    alarmData.setfAlarmType("03"); //01.老化柜 02.机械臂 03.老化层 04.点位
                                    alarmData.setfEquipmentCode(storeyInfo.getfStoreyCode());
                                    alarmData.setfAlarmData("断电失败");
                                    tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 上料：
     * 1.由页面向机械臂发送上料指令，通讯机械臂
     * 2.通讯老化柜，根据响应信息获取层状态
     * 3.老化层上电
     * 4.通讯老化层，分别获取72个点位状态，开始计时
     * 5.向点位写入时间，分别写入年、月、日、时、分
     */
    @GetMapping("/agingCabinet")
//    public void agingCabinetData(TStoreyInfo storeyInfo) throws Exception {
    public void agingCabinetData() throws Exception {
        // 获取老化层信息，将用页面传参代替此步骤
        Long id = Long.valueOf(1);
        TStoreyInfo storeyInfo = tStoreyInfoService.selectTStoreyInfoById(id);

        if (storeyInfo == null) {
            throw new Exception("无老化层信息，请检查老化层信息再上料！");
        }
        // 获取老化柜信息
        TEquipmentInfo equipmentInfo = tEquipmentInfoService.selectTEquipmentInfoById(storeyInfo.getfEquipmentId());
        if (equipmentInfo == null) {
            throw new Exception("无老化柜信息，请检查老化柜信息再上料！");
        }
        TEquipmentAlarmData alarmData = new TEquipmentAlarmData();

        /* 1.机械臂通讯 */


        /* 2.老化柜通讯 */
        String sendMsg1 = "00010000000601040000000A";
        String receiveMsg1 = agingCabinetCommunication(equipmentInfo, sendMsg1);
        if (receiveMsg1 == null || receiveMsg1.length() != 58) {
            // 记录异常数据
            alarmData.setfAlarmType("01"); //01.老化柜 02.机械臂 03.老化层 04.点位
            alarmData.setfEquipmentCode(equipmentInfo.getfEquipmentCode());
            alarmData.setfAlarmData("老化柜通讯失败");
            tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
            throw new Exception(storeyInfo.getfStoreyCode() + "号老化柜通讯故障，请重新尝试上电！");
        }
        // 解析服务器返回信息
        // 示例：00 01 00 00 00 17 01 04 14 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        String storeyCode = storeyInfo.getfStoreyCode();
        int code = Integer.parseInt(storeyCode.substring(storeyCode.indexOf("-") + 1));
        int startIndex;
        int endIndex;
        String status;
        int flag = 1;
        for (startIndex = 18, endIndex = 22, code = 1; endIndex <= receiveMsg1.length(); startIndex += 4, endIndex += 4, flag++) {
            status = receiveMsg1.substring(startIndex, endIndex);
            if (flag == code && "0001".equals(status)) {
                /* 3.老化层上电 */
                // 示例：000100000006 01 功能码05 寄存器地址0000 上电FF00
                String addr = Integer.toHexString(flag - 1).length() < 2 ? "0" + Integer.toHexString(flag - 1) : Integer.toHexString(flag - 1);
                String sendMsg2 = "000100000006010500" + addr + "FF00";
                String receiveMsg2 = storeyCommunication(storeyInfo, sendMsg2);
                if (!sendMsg2.equals(receiveMsg2) || receiveMsg2 == null) {
                    // 记录异常数据
                    alarmData.setfAlarmType("03"); //01.老化柜 02.机械臂 03.老化层 04.点位
                    alarmData.setfEquipmentCode(storeyInfo.getfStoreyCode());
                    alarmData.setfAlarmData("上电失败");
                    tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
                    throw new Exception(storeyInfo.getfStoreyCode() + "号老化层上电失败，请重新尝试上电！");
                }
                // 老化层更新状态
                storeyInfo.setfStatus("1"); //运行
                storeyInfo.setfAgingStartTime(new Date()); // 记录老化开始时间
                tStoreyInfoService.updateTStoreyInfo(storeyInfo);

                /* 4.老化层通讯，72个点位，1-27一个端口号、28-54一个端口号、55-72一个端口号，IP不变 */
                // 示例：000100000006 点位地址01 功能码03 起始位置0000 终止位置000A
                String sendMsg3 = "00010000000601030000000A";
                // 解析服务器返回信息
                // 示例：000100000017010314 浓度0000 状态0001 AD值01F7 标定AD值01F7 年07E9 月0005 日0004 时000E 分000F 秒0015
                // 获取主板最终状态值
                String receiveStatus = pointCommunication(storeyInfo, sendMsg3);
                if (receiveStatus == null) {
                    // 记录异常数据
                    alarmData.setfAlarmType("03"); //01.老化柜 02.机械臂 03.老化层 04.点位
                    alarmData.setfEquipmentCode(storeyInfo.getfStoreyCode());
                    alarmData.setfAlarmData("老化层通讯失败，无响应");
                    tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
                } else if("0000".equals(receiveStatus) || "0005".equals(receiveStatus)){
                    continue;
                }

                /* 5.写入时间，年、月、日、时、分 */
                // 示例：000100000006 设备地址01写时间06 寄存器地址0004（年04月05日06时07分08） 年/月/日/时/分数值07E9（2025）
                Calendar cal = Calendar.getInstance();
                // 当前年
                int y = cal.get(Calendar.YEAR);
                // 当前月
                int m = cal.get(Calendar.MONTH) + 1;
                // 当前日
                int d = cal.get(Calendar.DATE);
                // 当前小时
                int h = cal.get(Calendar.HOUR_OF_DAY);
                // 当前分钟
                int mm = cal.get(Calendar.MINUTE);
                String minutes = Integer.toHexString(mm).length() < 2 ? "000" + Integer.toHexString(mm) : "00" + Integer.toHexString(mm);
                String hours = Integer.toHexString(h).length() < 2 ? "000" + Integer.toHexString(h) : "00" + Integer.toHexString(h);
                String date = Integer.toHexString(d).length() < 2 ? "000" + Integer.toHexString(d) : "00" + Integer.toHexString(d);
                String month = Integer.toHexString(m).length() < 2 ? "000" + Integer.toHexString(m) : "00" + Integer.toHexString(m);
                String year = Integer.toHexString(y).length() < 4 ? "0" + Integer.toHexString(y) : Integer.toHexString(y);

                String sendMsgYear = "0001000000060" + code + "060004" + year;
                String receiveMsgYear = storeyCommunication(storeyInfo, sendMsgYear);
                if (!sendMsgYear.equals(receiveMsgYear) || receiveMsgYear == null) {
                    // 记录异常数据
                    alarmData.setfAlarmType("04"); //01.老化柜 02.机械臂 03.老化层 04.点位
                    alarmData.setfEquipmentCode(storeyInfo.getfStoreyCode());
                    alarmData.setfAlarmData("写入时间失败-年");
                    tEquipmentAlarmDataService.insertTEquipmentAlarmData(alarmData);
                }

            }
        }
    }

    // 老化柜通讯
    private String agingCabinetCommunication(TEquipmentInfo equipment, String sendMsg) {
        String receiveMsg = null;
        String ip = null;
        Integer port = null;
        int num = 1;
        try {
            ip = equipment.getfIp();
            port = equipment.getfPort();
            if (ip == null || port == null) {
                receiveMsg = equipment.getfEquipmentCode();
            } else {
                // 若通讯失败将再次通讯，最多通讯3次
                while ((receiveMsg == null || receiveMsg.length() != 58) && num <= 3) {
                    // 发送并接收数据
                    receiveMsg = TCPClient.PLCConnect(ip, port, sendMsg);
                    num++;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return receiveMsg;
    }

    //老化层通讯
    private String storeyCommunication(TStoreyInfo storey, String sendMsg) {
        String ip = null;
        Integer port = null;
        String receiveMsg = null;
        int num = 1;
        try {
            ip = storey.getfIp();
            port = Integer.parseInt(storey.getfPort()); // 使用 parseInt 确保字符串到整数的转换
            if (ip == null || port == null) {
                receiveMsg = storey.getfStoreyCode();
            }
            // 若通讯失败将再次通讯，最多通讯3次
            while ((!sendMsg.equals(receiveMsg) || receiveMsg == null) && num <= 3) {
                // 发送并接收数据
                receiveMsg = TCPClient.PLCConnect(ip, port, sendMsg);
                num++;
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return receiveMsg;
    }

    //点位数据采集
    private String pointCommunication(TStoreyInfo storey, String sendMsg) {
        String ip = null;
        Integer port = null;
        String status = null;
        int num = 1;
        try {
            ip = storey.getfIp();
            port = Integer.parseInt(storey.getfPort()); // 使用 parseInt 确保字符串到整数的转换
            String receiveMsg = null;
            if (ip == null || port == null) {
                return status;
            }
            // 若通讯失败将再次通讯，最多通讯3次
            while ((!"0001".equals(receiveMsg) || receiveMsg == null) && num <= 3) {
                // 发送并接收数据
                receiveMsg = TCPClient.PLCConnect(ip, port, sendMsg);
                if(receiveMsg != null){
                    status = receiveMsg.substring(22, 26); //板子状态
                }
                num++;
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return status;
    }
}
