package com.youchain.timingtask;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONString;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import com.youchain.basicdata.service.ZcjxbService;
import com.youchain.basicdata.service.dto.ZcjxbDto;
import com.youchain.basicdata.service.dto.ZcjxbQueryCriteria;
import com.youchain.basicdata.service.mapstruct.ZcjxbMapper;
import com.youchain.timingtask.service.ZCJXBService;
import com.youchain.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.InetAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class ZCJXBTask {
    private final RedisUtils redisUtils;
    private final ZcjxbService zcjxbService;
    private final ZCJXBService zcjxbServiceCZ;
    private final ZcjxbMapper zcjxbMapper;
//    private HashMap<String, ModbusMaster> hashMap = new HashMap<>();

    @Value("${task.onOff.zCJXBTask}")
    private boolean onOff;


    /**
     * 定时任务查询注塑机是否要叫料和反空车
     */
    public synchronized void zCJXBTask() throws Exception {
        if (!onOff) {
            //如果要在本地测试请关闭线上的定时任务执行
            //log.error("未启用");
            return;
        }
        //业务执行
        ZcjxbQueryCriteria zcjxbQueryCriteria = new ZcjxbQueryCriteria();
        zcjxbQueryCriteria.setEnabled(true);
        zcjxbQueryCriteria.setCommunicationType("MODBUSTCP");
        List<ZcjxbDto> zcjxbDtos = zcjxbService.queryAll(zcjxbQueryCriteria);
        if (zcjxbDtos.isEmpty()) {
            log.error("没有要监控的任务,请关闭定时任务======》机械臂定时查询");
            return;
            //throw new RuntimeException("没有要监控的任务,需要手动启用");
        }
        for (ZcjxbDto z : zcjxbDtos) {//循环遍历
            //连接modbus连接失败
            ModbusMaster modbusMaster = null;
            //读取数据
            int readV = 0;
            try {
                modbusMaster = this.initConnect(z.getIp(), z.getPort());
            } catch (Exception e) {
                log.error("modbus连接失败【{}===》{}】", z.getIp(), z.getPort());
                z.setErrorTxt("modbus连接失败"+z.getIp()+"=>"+z.getPort());
                zcjxbService.update(zcjxbMapper.toEntity(z));
                continue;
            }
            try {
                readV = this.readHoldingRegisters(modbusMaster, z.getModbusTcpSlaveid(), z.getAddress());
                if (readV > 4) {
                    log.error("modbus读取结果出现异常【{}===={}=》{}】", z.getIp(), z.getModbusTcpPoint(), readV);
                    z.setErrorTxt("modbus读取结果出现异常"+z.getIp()+"=>"+z.getPort()+"值"+readV);
                    z.setReadValue(readV);
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                    //modbus关闭连接
                    this.closeConnect(modbusMaster);
                    continue;
                }
            } catch (Exception e) {
                log.info("modbus连接成功,读取地址出现错误");
                z.setErrorTxt("modbus连接成功,读取地址出现错误");
                zcjxbService.update(zcjxbMapper.toEntity(z));
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                return;
            }
            //正常状态  1或0  状态为 未启用
            if ((readV == 1 || readV == 0) && !z.getModbusTcpTaskFinish()) {
                if (z.getErrorTxt() == null || z.getErrorTxt().length()>1){
                    z.setErrorTxt("");
                    z.setReadValue(readV);
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                }
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            }
            //回复正常 1或0  状态为 启用启用
            if ((readV == 1 || readV == 0)) {
                log.info("任务完成，回复正常状态");
                z.setModbusTcpTaskFinish(false);
                z.setTaskName("无任务");
                z.setReadValue(readV);
                z.setErrorTxt("");
                zcjxbService.update(zcjxbMapper.toEntity(z));
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            }
            //返完空车 2  是否启用为false且任务状态为无任务
            if (readV == 2 && (!z.getModbusTcpTaskFinish() && z.getTaskName().equals("无任务"))) {
                //返回空车
                log.info("【{}-{}】返回空车", z.getIp(), z.getModbusTcpPoint());
                if (zcjxbServiceCZ.zcJxbFKC(z)) {
                    z.setModbusTcpTaskFinish(true);
                    z.setTaskName("返空车");
                    z.setReadValue(readV);
                    z.setErrorTxt("");
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                } else {
                    z.setModbusTcpTaskFinish(false);
                    z.setTaskName("无任务");
                    z.setReadValue(readV);
                    Object o = redisUtils.get("fkcData", null);
                    z.setErrorTxt(o.toString());
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                }
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            }
            //返完空车叫料
            if (readV == 4 && (z.getModbusTcpTaskFinish() && (z.getTaskName().equals("返空车") || z.getTaskName().equals("无任务")))) {
                //叫料
                log.info("【{}--->{}】叫料", z.getIp(), z.getModbusTcpPoint());
                if (zcjxbServiceCZ.zcJxbJMC(z)) {
                    z.setModbusTcpTaskFinish(true);
                    z.setTaskName("叫料");
                    z.setReadValue(readV);
                    z.setErrorTxt("");
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                } else {
                    z.setModbusTcpTaskFinish(true);
                    z.setTaskName("返空车");
                    z.setReadValue(readV);
                    Object o = redisUtils.get("jlData", null);
                    z.setErrorTxt(o.toString());
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                }
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            }
            //已有任务
            if (readV == 2 && z.getModbusTcpTaskFinish()) {//该点位已经有任务
                //log.info("{}=>该点位已经有任务 是空车回库=>正在工作", z.getModbusTcpPoint());
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            }
            //已有任务
            if (readV == 4 && z.getModbusTcpTaskFinish()) {//该点位已经有任务
                //log.info("{}=>该点位已经有任务 是叫料的任务=>正在工作", z.getModbusTcpPoint());
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            }
            if (readV == 2) {
                //返回空车
                log.info("【{}-{}】重新返回空车", z.getIp(), z.getModbusTcpPoint());
                if (zcjxbServiceCZ.zcJxbFKC(z)) {
                    z.setModbusTcpTaskFinish(true);
                    z.setTaskName("返空车");
                    z.setReadValue(readV);
                    z.setErrorTxt("");
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                } else {
                    z.setModbusTcpTaskFinish(false);
                    z.setTaskName("无任务");
                    z.setReadValue(readV);
                    Object o = redisUtils.get("fkcData", null);
                    z.setErrorTxt(o.toString());
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                }
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            } else if (readV == 4) {
                //叫料
                log.info("【{}-{}】重新叫料", z.getIp(), z.getModbusTcpPoint());
                if (zcjxbServiceCZ.zcJxbJMC(z)) {
                    z.setModbusTcpTaskFinish(true);
                    z.setTaskName("叫料");
                    z.setReadValue(readV);
                    z.setErrorTxt("");
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                } else {
                    z.setModbusTcpTaskFinish(true);
                    z.setTaskName("返空车");
                    z.setReadValue(readV);
                    Object o = redisUtils.get("jlData", null);
                    z.setErrorTxt(o.toString());
                    zcjxbService.update(zcjxbMapper.toEntity(z));
                }
                //modbus关闭连接
                this.closeConnect(modbusMaster);
                continue;
            }
            log.error("【{}-》{}】出现异常 读取的结果是{}，内容因该是0、1、2、4", z.getIp(), z.getModbusTcpPoint(), readV);
        }
    }

    /**
     * 初始化连接modbus Master主机
     */
    public ModbusMaster initConnect(String ip, Integer port) throws Exception {
        // 设置主机TCP参数
        TcpParameters tcpParameters = new TcpParameters();
        // 设置TCP的ip地址
        InetAddress address = InetAddress.getByName(ip);

        /**
         * 如果是本机localhost的话，可以直接使用
         * tcpParameters.setHost(InetAddress.getLocalHost());
         */

        // TCP参数设置ip地址
        tcpParameters.setHost(address);
        // TCP设置长连接
        tcpParameters.setKeepAlive(true);
        // TCP设置端口，这里设置是默认端口502
        tcpParameters.setPort(port);

        // 创建一个主机
        ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
        // 设置事务ID自动递增
        Modbus.setAutoIncrementTransactionId(true);
        if (!master.isConnected()) {
            // 开启连接
            master.connect();
        }
        return master;
    }

    /**
     * 关闭连接
     */
    public void closeConnect(ModbusMaster modbusMaster) throws Exception {
        modbusMaster.disconnect();
        Thread.sleep(1500);
    }

    /**
     * 批量读取HoldingRegisters寄存器（0x03）数据
     *
     * @param slaveId 从机ID
     * @param offset  开始地址
     */
    public int readHoldingRegisters(ModbusMaster modbusMaster, int slaveId, int offset) throws Exception {
        int[] registerValues = modbusMaster.readHoldingRegisters(slaveId, offset, 1);
        return registerValues[0];
    }
}

