package com.mvc.service.aihcxd;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.mvc.constant.DeviceType;
import com.mvc.constant.GasMeterSpec;
import com.mvc.entity.TAiTargetbox;
import com.mvc.entity.TempAiread;
import com.mvc.entity.TempServerIdenCfg;
import com.mvc.model.*;
import com.mvc.model.aihcxd.*;
import com.mvc.model.aiiden.AiIdenMap;
import com.mvc.model.aiiden.AiTokenModel;
import com.mvc.netty.dealconstant.HandleMsgCode;
import com.mvc.netty.dealconstant.ResourcePath;
import com.mvc.netty.dealconstant.ResourceTag;
import com.mvc.recognition.AiConstant;
import com.mvc.recognition.freeai.FreeAiRecognition;
import com.mvc.recognition.predict.DataPredict;
import com.mvc.service.TMarkRecordService;
import com.mvc.service.aiiden.DnType;
import com.mvc.service.aiiden.TaskReviewService;
import com.mvc.service.aiiden.UnitType;
import com.mvc.service.copy.TAireadService;
import com.mvc.service.copy.TServerReadService;
import com.mvc.service.taizhang.DeviceAccService;
import com.mvc.service.taizhang.StationAccService;
import com.mvc.service.taizhang.TAicbqCfgService;
import com.mvc.service.temp.TempAireadService;
import com.mvc.service.temp.TempServerIdenCfgService;
import com.mvc.utils.*;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class WmIdenService {
    private static Logger log = LoggerFactory.getLogger(WmIdenService.class);
    private final String URL_WM_IDEN = "/api/v1/ai/watermeters/iden";//水表识别接口地址
    //private final String URL_PG_IDEN = "/api/v1/ai/pgmeters/iden";//压力表识别接口地址
    //private final String URL_LED_IDEN = "/api/v1/ai/ledmeters/iden";//液晶表识别接口地址
    private final String URL_PG_IDEN = "/PgMeterAiController/getIdentResult";//压力表识别接口地址
    private final String URL_LED_IDEN = "/LedMeterAiController/getIdentResult";//液晶表识别接口地址
    private final String URL_AUTH_LOGIN = "/api/v1/auth/login?username=admin&password=123456";
    private static AiTokenModel aiTokenModel;                                               //存储当前有效的Token
    private static int api_token_validity = 1800;                                             //token有效期30分钟

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private Environment env;

    @Resource
    private TAicbqCfgService tAicbqCfgService;

    @Resource
    private StationAccService stationAccService;

    @Resource
    private TServerReadService tServerReadService;

    @Resource
    private TAiTargetBoxService tAiTargetBoxService;

    @Resource
    private TAireadService tAireadService;

    @Resource
    private TaskReviewService taskReviewService;

    @Resource
    private TMarkRecordService tMarkRecordService;

    @Resource
    private DeviceAccService deviceAccService;

    @Resource
    private TempServerIdenCfgService tempServerIdenCfgService;

    @Resource
    private TempAireadService tempAireadService;

    /*@Resource
    private NoConfiguredRecognition noConfiguredRecognition;*/

    @Resource
    private FreeAiRecognition freeAiRecognition;

    @Resource
    private DataPredict dataPredict;

    @Async
    public TServerReadModel getIdenResult(TAireadModel tAireadModel) {
        if (tAireadModel.getDeviceSn() == null) {//有此情况 2024-06-15 guozhao
            return null;
        }
        log.info("服务侧识别SN:" + tAireadModel.getDeviceSn());
        if (!ImageUtil.isImageByExtension(tAireadModel.getSmallJpgUrl()) && !ImageUtil.isImageByExtension(tAireadModel.getBigJpgUrl())) {
            log.info("无图片,不进行服务侧识别:" + tAireadModel.getDeviceSn());
            return null;
        }
        String deviceType = tAireadModel.getDeviceType();

        //获取设备台账(为了获取设备类型,现在水表和压力表是在报文中借用密码来区分,但LED还没有处理,因此从台账查询) 2023-07-01 guozhao
        DeviceAccModel accModel = deviceAccService.findDeviceAccBysn(tAireadModel.getDeviceSn());
        if (accModel != null) {
            deviceType = accModel.getDeviceType();
        }
        //大模型还不完善,仍使用SSD模型 2024-01-08 guozhao
        /*if (deviceType.equals("9")) {//服务侧识别设备,调用大模型(大模型不需要传入参数) 2023-11-08 guozhao
            return onnxIdenService.postInParam(tAireadModel);
        }*/
        TAicbqCfgModel cfg = null;

        /** 将水表和燃气表切换为免配置识别 2025-01-16 guozhao */
        if (deviceType.equals(DeviceType.WATER_METER.getCode()) || deviceType.equals(DeviceType.GAS_METER.getCode())) {
            //又一个版本,与MCU逻辑一致的免配置服务侧识别 2024-08-26 guozhao
            List<TAicbqCfgModel> cfgList = tAicbqCfgService.findTAicbqCfg(tAireadModel.getDeviceSn(), "", "1");
            cfg = cfgList == null || cfgList.size() == 0 ? null : cfgList.get(0);
            freeAiRecognition.handleFreeAi(tAireadModel, cfg);
            return null;
        }

        /*List<TAicbqCfgModel> cfgList = tAicbqCfgService.findTAicbqCfg(tAireadModel.getDeviceSn(), "", "1");
        if (cfgList == null || cfgList.size() == 0) {//没有配置
            return null;
        }
        TAicbqCfgModel cfg = cfgList.get(0);*/

        Object inModel = null;
        String imgPath = "";
        if (deviceType.equals(DeviceType.WATER_METER.getCode()) || deviceType.equals(DeviceType.GAS_METER.getCode())) {
            //发现有异常的配置信息(SN:00000001628) 2024-03-21 guozhao
            if (cfg.getDigitNum() == null || cfg.getDnType() == null) {
                log.warn(String.format("[%s]服务侧识别,有配置但值=null", cfg.getDeviceSn()));
                return null;
            }

            WmInModel wmInModel = getWmInModelByTairead(tAireadModel, cfg, deviceType);
            if (wmInModel == null || StringUtils.isEmpty(wmInModel.getImagePath())) {
                return null;
            }
            imgPath = wmInModel.getImagePath();
            inModel = wmInModel;
        } else if (deviceType.equals(DeviceType.PRESSURE_GAGE.getCode())) {
            PgNoCfgInModel pgNoCfgInModel = getPgNoCfgInModelByTairead(tAireadModel);
            imgPath = pgNoCfgInModel.getImagePath();
            inModel = pgNoCfgInModel;

            /* 乔老师版本-应该是不再使用、维护了 2024-07-03 guozhao
            PgInModel pgInModel = getPgInModelByTairead(tAireadModel, cfg);
            imgPath = pgInModel.getImagePath();
            inModel = pgInModel;*/
        } else if (deviceType.equals(DeviceType.LED_METER.getCode())) {
            LedNoCfgInModel ledNoCfgInModel = getLedNoCfgInModelByTairead(tAireadModel);
            imgPath = ledNoCfgInModel.getImagePath();
            inModel = ledNoCfgInModel;

            /* 乔老师版本-应该是不再使用、维护了 2024-07-03 guozhao
            LedInModel ledInModel = getLedInModelByTairead(tAireadModel, cfg);
            imgPath = ledInModel.getImagePath();
            inModel = ledInModel;*/
        }

        if (inModel == null) return null;

        ResultMap resultMap = postInParam(inModel, imgPath, deviceType);

        if (inModel instanceof WmInModel) {
            dataPredict.handleDataPredict(tAireadModel, ((WmInModel) inModel).getNumberUnit());
        }

        TServerReadModel tServerReadModel = new TServerReadModel();
        ArrayList<TAiTargetbox> finalList = null;
        tAireadModel.setDeviceType(deviceType);//为了特殊处理LED表和水表DebugInfo的区别 2023-09-01 guozhao
        if (resultMap == null || !resultMap.getFlag().equals("1")) return null;
        if (deviceType.equals(DeviceType.WATER_METER.getCode()) || deviceType.equals(DeviceType.GAS_METER.getCode())) {
            WmOutModel outModel = JSON.toJavaObject((JSONObject) resultMap.getRes(), WmOutModel.class);
            tServerReadModel = getServerReadModel(tAireadModel, cfg, outModel.getAiValue(), outModel.getServerRotateAngle(), outModel.getServerNumberRegion(),
                    outModel.getOutImagePath(), outModel.getSmallJPEGPath(), outModel.getServerAiValueIsOk(), outModel.getDebugInfo(), false);
            finalList = outModel.getFinalList();
            //预留旋转图像
            if (deviceType.equals(DeviceType.GAS_METER.getCode())) {
                WmInModel wmInModel = (WmInModel) inModel;
                rotatingImage(imgPath, String.valueOf(wmInModel.getIs_updown()));
            }
        } else if (deviceType.equals(DeviceType.PRESSURE_GAGE.getCode())) {
            PgNoCfgOutModel outModel = JSON.toJavaObject((JSONObject) resultMap.getRes(), PgNoCfgOutModel.class);

            if (outModel == null || outModel.getAi_in_Value() == null) {
                return null;
            }
            finalList = outModel.getPg_nms_list();
            tServerReadModel = getServerReadModel(tAireadModel, cfg, outModel.getAi_in_Value(), outModel.getServerRotateAngle(),
                    ArrayUtils.toPrimitive(outModel.getServerNumberRegion()), outModel.getOutImagePath(), "",
                    0, outModel.getDebugInfo(), true);

            //压力表的外圈值 2024-07-16 guozhao
            tServerReadModel.setExtendValue(new BigDecimal(outModel.getAi_out_Value()));
        } else if (deviceType.equals(DeviceType.LED_METER.getCode())) {
            LedNoCfgOutModel outModel = JSON.toJavaObject((JSONObject) resultMap.getRes(), LedNoCfgOutModel.class);
            if (outModel == null || StringUtils.isEmpty(outModel.getAiValue())) {
                return null;
            }
            //来的是字符串,非合法值不处理
            Double value = -1D;
            try {
                value = Double.valueOf(outModel.getAiValue());
            } catch (Exception e) {
                return null;
            }
            Integer serverRotateAngle = null;
            if (StringUtils.isNumeric(outModel.getServerRotateAngle())) {
                serverRotateAngle = Integer.valueOf(outModel.getServerRotateAngle());
            }
            tServerReadModel = getServerReadModel(tAireadModel, cfg, value, serverRotateAngle,
                    ArrayUtils.toPrimitive(outModel.getServerNumberRegion()), outModel.getOutImagePath(), "", 0, outModel.getDebugInfo2(), true);
        }

        /* 乔老师版本-应该是不再使用、维护了 2024-07-03 guozhao
        else if (deviceType.equals(DeviceType.PRESSURE_GAGE.getCode())) {
            PgOutModel outModel = JSON.toJavaObject((JSONObject) resultMap.getRes(), PgOutModel.class);
            tServerReadModel = getServerReadModel(tAireadModel, cfg, outModel.getAiValue(), null, null,
                    outModel.getOutImagePath(), "", outModel.getServerAiValueIsOk(), outModel.getDebugInfo());
            finalList = outModel.getFinalList();
        } else if (deviceType.equals(DeviceType.LED_METER.getCode())) {
            LedOutModel outModel = JSON.toJavaObject((JSONObject) resultMap.getRes(), LedOutModel.class);
            tServerReadModel = getServerReadModel(tAireadModel, cfg, outModel.getAiValue(), null, null,
                    outModel.getOutImagePath(), "", outModel.getServerAiValueIsOk(), outModel.getDebugInfo());
            finalList = outModel.getFinalList();
        }*/

        tServerReadModel = tServerReadService.saveTServerRead(tServerReadModel);
        if (tAireadModel.getFunCode().equals(HandleMsgCode.H_32.getCode())) {//如果没有MCU的识别值,则更新ResultValue为服务侧值
            if (tAireadModel.getIdentValue() == null && tAireadModel.getRawValue() == null) {
                tAireadModel.setResultValue(tServerReadModel.getIdentValue());
            } else {
                TMarkRecordModel markRecordModel = tMarkRecordService.findUntreatedModelByStationCode(tAireadModel.getStationCode());
                if (markRecordModel != null && markRecordModel.getReviewFlag().equals("1")) {//标记使用服务器侧算法
                    tAireadModel.setResultValue(tServerReadModel.getIdentValue());
                }
            }
            tAireadService.updateTAiread(tAireadModel);
        }

        if (finalList != null) {
            for (TAiTargetbox targetbox : finalList) {
                targetbox.setAiType("1");
                targetbox.setSourceId(tServerReadModel.getServerReadId());
                tAiTargetBoxService.saveTAiTargetBox(targetbox);
            }
        }
        //产生识别结果后,燃气表放入报警处理队列 2024-02-06 guozhao
        try {
            if (tServerReadModel != null && deviceType.equals(DeviceType.GAS_METER.getCode())) {
                StoreBlockingQueue.gasAlarmQueue.put(tServerReadModel);
            }
            //免配置识别 2024-03-05 guozhao (识别服务有挂掉的情况,先注释观察)
            /*if (inModel instanceof WmInModel) {
                noConfiguredRecognition.handleNoConfigRecognition((WmInModel) inModel,tAireadModel);
            }*/
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error(ex.getMessage());
        }

        return tServerReadModel;
    }

    private TServerReadModel getServerReadModel(TAireadModel tAireadModel, TAicbqCfgModel cfg, Double aiValue, Integer angle, int[] serverNumberRegion,
                                                String outImagePath, String smallJPEGPath, Integer serverAiValueIsOk, int[][] debugInfo, boolean isNoCfg) {
        TServerReadModel tServerReadModel = new TServerReadModel();
        tServerReadModel.setDeviceSn(tAireadModel.getDeviceSn());
        tServerReadModel.setStationCode(tAireadModel.getStationCode());
        tServerReadModel.setAireadId(tAireadModel.getAireadId());

        BigDecimal value = new BigDecimal(aiValue);

        //免配置版本不处理小数 2024-07-04 guozhao
        if (!isNoCfg) {
            int num = value.stripTrailingZeros().scale();
            if (cfg.getUnitType() != null) {
                num = cfg.getUnitType() < 4 ? 4 - cfg.getUnitType() : 0;
            }

            if (value != null) {
                value = value.setScale(num, RoundingMode.HALF_UP);
            }
        }

        tServerReadModel.setIdentValue(value);

        String jpgUrl = outImagePath;//直接使用的是服务端的Url
        tServerReadModel.setJpgUrl(jpgUrl);
        log.info("读数:" + aiValue + ";标注图:" + jpgUrl);
        tServerReadModel.setRotateAngle(angle);//角度
        if (serverNumberRegion != null) {//数字区域坐标
            tServerReadModel.setNumberRegion(StringUtils.join(serverNumberRegion, ','));
        }

        if (StringUtils.isNotEmpty(smallJPEGPath)) {
            String fileName = smallJPEGPath;
            tServerReadModel.setSmallJpgUrl(smallJPEGPath);
            int index0 = fileName.indexOf("small");
            fileName = fileName.substring(index0, fileName.length() - 4);
            String[] temp = fileName.split("_");
            if (temp != null && temp.length > 3) {
                tServerReadModel.setSmallJpgType(temp[1]);
                tServerReadModel.setSmallJpgHeight(Integer.valueOf(temp[2]));
                tServerReadModel.setSmallJpgWidth(Integer.valueOf(temp[3]));
            }

            //下载小图(项目不存储小图,只依赖服务侧的URL不是太灵活,因此也存一下) 2023-08-28 guozhao
            String date = DateUtil.formateDate(tAireadModel.getSampleTime(), "yyyy-MM");
            StringBuilder sb = new StringBuilder();
            sb.append(env.getProperty(ResourceTag.UploadPathTag.getTag()));
            sb.append(ResourcePath.Rawdata.getPath());
            sb.append(date);
            sb.append("/");
            sb.append(tAireadModel.getStationCode());
            try {
                fileName = DateUtil.formateDate(tAireadModel.getSampleTime(), "yyyyMMddHHmmss_") + fileName;//防重
                int index = smallJPEGPath.lastIndexOf('.');
                if (index > 0) {
                    fileName = fileName + smallJPEGPath.substring(index);
                }
                FileUtil.download(smallJPEGPath, sb.toString(), fileName);
            } catch (IOException e) {
                e.printStackTrace();
            }
            tServerReadModel.setSmallJpgUrl(date + "/" + tAireadModel.getStationCode() + "/" + fileName);
        }
        tServerReadModel.setGoodFlag(serverAiValueIsOk + "");
        if (debugInfo != null && debugInfo.length == 2) {
            int[] arrSzsbz = new int[debugInfo[0].length];
            int[] arrSzsbgl = new int[debugInfo[1].length];
            System.arraycopy(debugInfo[0], 0, arrSzsbz, 0, arrSzsbz.length);
            System.arraycopy(debugInfo[1], 0, arrSzsbgl, 0, arrSzsbgl.length);

            //处理调试信息中的88数值(无实际意义)
            if (tAireadModel.getDeviceType().equals(DeviceType.LED_METER.getCode())) {//LED表是888代表无实际意义
                tServerReadModel.setSzsbz(StringUtils.join(arrSzsbz, ',').replace("888", "-"));
                tServerReadModel.setSzsbgl(StringUtils.join(arrSzsbgl, ',').replace("888", "-"));
            } else {
                tServerReadModel.setSzsbz(StringUtils.join(arrSzsbz, ',').replace("88", "-"));
                tServerReadModel.setSzsbgl(StringUtils.join(arrSzsbgl, ',').replace("88", "-"));
            }

        }
        return tServerReadModel;
    }

    /**
     * 根据当前读数获取压力表服务侧的输入对象
     *
     * @param tAireadModel
     * @param cfg
     * @return
     */
    private PgInModel getPgInModelByTairead(TAireadModel tAireadModel, TAicbqCfgModel cfg) {
        PgInModel inModel = new PgInModel();
        String inImg = StringUtils.isNotEmpty(tAireadModel.getBigJpgUrl()) ? tAireadModel.getBigJpgUrl() : tAireadModel.getSmallJpgUrl();
        String dirDate = DateUtil.getCurrentTime("yyyy-MM");
        String outPath = env.getProperty(ResourceTag.UploadPathTag.getTag()) + "server/" + dirDate + "/" + tAireadModel.getDeviceSn() + "/";
        File outDir = new File(outPath);
        if (!outDir.exists()) {
            outDir.mkdirs();
        }

        inModel.setImagePath(convertSmallUrl(inImg));
        inModel.setOutputPath(outPath);
        inModel.setRANGE_MIN(cfg.getPgRangeMin().doubleValue());
        inModel.setRANGE_MAX(cfg.getPgRangeMax().doubleValue());

        //加载上一次的DebugInfo，如果是没有历史DebugInfo，则全设为888，否则读取并设置为历史DebugInfo
        TServerReadModel serverReadModel = tServerReadService.findPreModelByDeviceSn(tAireadModel);
        if (serverReadModel == null) {
            inModel.setLastDebugInfo(new int[][]{{888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}, {888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}});
        } else {
            inModel.setLastDebugInfo(getDebugInfo(serverReadModel.getSzsbz(), serverReadModel.getSzsbgl()));
        }
        //是否使用手动配置(当前条件是有配置区域,并且手动配置的等级是高的情况下)

        if (StringUtils.isNotEmpty(cfg.getPgManualConfig()) && StringUtils.isNotEmpty(cfg.getPgManualConfigLevel()) && cfg.getPgManualConfigLevel().equals("1")) {
            inModel.setIsUseConfig(true);
            inModel.setmanualConfigLevel(1);
        } else {
            inModel.setIsUseConfig(false);
            inModel.setmanualConfigLevel(0);
        }

        //设置手动配置参数，不论paramPgInModel.IsUseConfig是true还是false，都要传入paramPgInModel.ConfigNumberRegion参数
        //注意数据库中paramPgInModel.ConfigNumberRegion初始值设为{0, 0, 0, 0, 0, 0}，注意 注意 注意 注意 注意
        inModel.setConfigNumberRegion(0, cfg.getOriginx() == null ? 0 : cfg.getOriginx());
        inModel.setConfigNumberRegion(1, cfg.getOriginy() == null ? 0 : cfg.getOriginy());
        inModel.setConfigNumberRegion(2, cfg.getEndx() == null ? 0 : cfg.getEndx());
        inModel.setConfigNumberRegion(3, cfg.getEndy() == null ? 0 : cfg.getEndy());
        inModel.setConfigNumberRegion(4, cfg.getCenterx() == null ? 0 : cfg.getCenterx());
        inModel.setConfigNumberRegion(5, cfg.getCentery() == null ? 0 : cfg.getCentery());
        return inModel;
    }

    /**
     * 获取二维调试信息
     *
     * @param szsbz
     * @param szsbgl
     * @return
     */
    private int[][] getDebugInfo(String szsbz, String szsbgl) {
        if (StringUtils.isEmpty(szsbz) || StringUtils.isEmpty(szsbgl)) {
            return new int[][]{{888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}, {888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}};
        } else {
            String[] arr1 = szsbz.split(",");
            String[] arr2 = szsbgl.split(",");

            int[][] result = new int[2][arr1.length];

            for (int i = 0; i < arr1.length; i++) {
                String str1 = arr1[i].trim();
                String str2 = arr2[i].trim();

                //入库的时候,将888转为了 - ,给识别服务的时候,再处理一下 2023-09-01 guozhao
                str1 = str1.equals("-") ? "888" : str1;
                str2 = str2.equals("-") ? "888" : str2;

                result[0][i] = Integer.parseInt(str1);
                result[1][i] = Integer.parseInt(str2);
            }

            return result;
        }
    }

    /**
     * 根据当前读数和配置获取液晶表服务侧的输入对象
     *
     * @param tAireadModel
     * @param cfg
     * @return
     */
    private LedInModel getLedInModelByTairead(TAireadModel tAireadModel, TAicbqCfgModel cfg) {
        LedInModel inModel = new LedInModel();
        String inImg = StringUtils.isNotEmpty(tAireadModel.getBigJpgUrl()) ? tAireadModel.getBigJpgUrl() : tAireadModel.getSmallJpgUrl();
        String dirDate = DateUtil.getCurrentTime("yyyy-MM");
        String outPath = env.getProperty(ResourceTag.UploadPathTag.getTag()) + "server/" + dirDate + "/" + tAireadModel.getDeviceSn() + "/";
        File outDir = new File(outPath);
        if (!outDir.exists()) {
            outDir.mkdirs();
        }
        inModel.setImagePath(convertSmallUrl(inImg));
        inModel.setOutputPath(outPath);
        ////inModel.setNumNumbers();//暂且没有用,整数部分数字个数，如000020.93的整数部分为2；如果初始值是0.025，则整数部分设为1，安装时需要人工配置。对于新安装的设备，如果没有人工配置初值，默认值设为0即可
        inModel.setNumberUnit(UnitType.getElement(cfg.getUnitType()).getValue());//读数精度(0.01)
        inModel.setMeterDiameter(DnType.getElement(cfg.getDnType()).getType());//口径(DN80)

        //乔老师暂时约定使用默认值 2023-08-31 guozhao
        inModel.setIsUseConfig(true);
        //inModel.setConfigRotateAngle(cfg.getImageLeanAngle() == null ? 0 : cfg.getImageLeanAngle());
        inModel.setConfigRotateAngle(0);//MCU侧旋转完了,服务侧不需要再旋转 2023-09-13 guozhao
        inModel.setConfigNumberRegion(0, 888);
        inModel.setConfigNumberRegion(1, 888);
        inModel.setConfigNumberRegion(2, 888);
        inModel.setConfigNumberRegion(3, 888);
        /*inModel.setIsUseConfig(cfg.getImageConfigFlag().equals("1"));
        inModel.setConfigRotateAngle(cfg.getImageLeanAngle());
        inModel.setConfigNumberRegion(0, cfg.getImageNumRegionXMin() == null ? 888 : cfg.getImageNumRegionXMin());
        inModel.setConfigNumberRegion(1, cfg.getImageNumRegionYMin() == null ? 888 : cfg.getImageNumRegionYMin());
        inModel.setConfigNumberRegion(2, cfg.getImageNumRegionXMax() == null ? 888 : cfg.getImageNumRegionXMax());
        inModel.setConfigNumberRegion(3, cfg.getImageNumRegionYMax() == null ? 888 : cfg.getImageNumRegionYMax());*/
        inModel.setLastDebugInfo(new int[][]{{888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}, {888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}});

        BigDecimal lastValue = null;
        Date lastTime = null;
        TServerReadModel lastServerRead = tServerReadService.findPreGoodModelByDeviceSn(tAireadModel);
        TaskReviewModel reviewModel = taskReviewService.findLastByDeviceSn(tAireadModel.getDeviceSn());
        if (lastServerRead != null) {
            inModel.setLastDebugInfo(getDebugInfo(lastServerRead.getSzsbz(), lastServerRead.getSzsbgl()));
            TAireadModel aireadModel = tAireadService.getTaireadByAireadId(lastServerRead.getAireadId());
            if (reviewModel != null && reviewModel.getReviewValue() != null && reviewModel.getSampleTime().compareTo(aireadModel.getSampleTime()) > -1) {//优先审核值
                lastValue = reviewModel.getReviewValue();
                lastTime = reviewModel.getSampleTime();
            } else {
                lastValue = lastServerRead.getIdentValue();
                lastTime = aireadModel.getSampleTime();
            }
        } else {//如果服务侧从未识别过,或识别是错误的,从审核记录中找最新的 2023-03-30 guozhao
            if (reviewModel != null) {
                lastValue = reviewModel.getReviewValue();
                lastTime = reviewModel.getSampleTime();
            } else {//即没有服务侧又没有审核值,这种情况先用上次MCU的,不然这个设备一直都没有服务侧值  2023-07-04 guozhao
                TAireadModel aireadModel = tAireadService.findPreGoodModelByDeviceSn(tAireadModel);
                if (aireadModel != null) {
                    lastValue = aireadModel.getResultValue();
                    lastTime = aireadModel.getSampleTime();
                }
            }
        }

        //MCU或服务侧都没有可靠的上次值
        if (lastValue == null || lastTime == null) {
            return null;
        }
        inModel.setLastOkValue(lastValue.doubleValue());
        inModel.setLastOkValueTime(DateUtil.formateDate(lastTime, "yyyy-MM-dd HH:mm:ss"));

        //加载上一次的DebugInfo，如果是没有历史DebugInfo，则全设为888，否则读取并设置为历史DebugInfo
        TServerReadModel serverReadModel = tServerReadService.findPreGoodModelByDeviceSn(tAireadModel);
        if (serverReadModel == null) {
            inModel.setLastDebugInfo(new int[][]{{888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}, {888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888, 888}});
        } else {
            inModel.setLastDebugInfo(getDebugInfo(serverReadModel.getSzsbz(), serverReadModel.getSzsbgl()));
        }
        return inModel;
    }

    private PgNoCfgInModel getPgNoCfgInModelByTairead(TAireadModel tAireadModel) {
        PgNoCfgInModel inModel = new PgNoCfgInModel();
        String inImg = StringUtils.isNotEmpty(tAireadModel.getBigJpgUrl()) ? tAireadModel.getBigJpgUrl() : tAireadModel.getSmallJpgUrl();
        String dirDate = DateUtil.getCurrentTime("yyyy-MM");
        String outPath = env.getProperty(ResourceTag.UploadPathTag.getTag()) + "server/" + dirDate + "/" + tAireadModel.getDeviceSn() + "/";
        File outDir = new File(outPath);
        if (!outDir.exists()) {
            outDir.mkdirs();
        }
        inModel.setRotateAngle(AiConstant.IMPOSSIBLE_VALUE);
        TServerReadModel serverReadModel = tServerReadService.findPreModelByDeviceSn(tAireadModel);
        if (serverReadModel != null) {
            if (serverReadModel.getRotateAngle() != null) {
                inModel.setRotateAngle(serverReadModel.getRotateAngle());
            }
            String region = serverReadModel.getNumberRegion();
            if (StringUtils.isNotEmpty(region) && region.contains(",")) {
                Integer[] regionArr = StringUtil.stringArrayToIntArray(region.split(","));
                if (regionArr == null) {
                    regionArr = AiConstant.NUMBER_REGION;
                }
                inModel.setInputNumberRegion(regionArr);
            }
        }
        inModel.setImagePath(convertSmallUrl(inImg));
        inModel.setOutputPath(outPath);
        return inModel;
    }

    private LedNoCfgInModel getLedNoCfgInModelByTairead(TAireadModel tAireadModel) {
        LedNoCfgInModel inModel = new LedNoCfgInModel();
        String inImg = StringUtils.isNotEmpty(tAireadModel.getBigJpgUrl()) ? tAireadModel.getBigJpgUrl() : tAireadModel.getSmallJpgUrl();
        String dirDate = DateUtil.getCurrentTime("yyyy-MM");
        String outPath = env.getProperty(ResourceTag.UploadPathTag.getTag()) + "server/" + dirDate + "/" + tAireadModel.getDeviceSn() + "/";
        File outDir = new File(outPath);
        if (!outDir.exists()) {
            outDir.mkdirs();
        }

        inModel.setServerLastOkRotateAngle(AiConstant.IMPOSSIBLE_VALUE);
        TServerReadModel serverReadModel = tServerReadService.findPreModelByDeviceSn(tAireadModel);
        if (serverReadModel != null) {
            if (serverReadModel.getRotateAngle() != null) {
                inModel.setServerLastOkRotateAngle(serverReadModel.getRotateAngle());
            }
            String region = serverReadModel.getNumberRegion();
            if (StringUtils.isNotEmpty(region) && region.contains(",")) {
                Integer[] regionArr = StringUtil.stringArrayToIntArray(region.split(","));
                if (regionArr == null) {
                    regionArr = AiConstant.NUMBER_REGION;
                }
                inModel.setInputNumberRegion(regionArr);
            }
        }

        inModel.setImagePath(convertSmallUrl(inImg));
        inModel.setOutputPath(outPath);
        return inModel;
    }

    /**
     * 根据读数和配置获取水表服务侧输入对象
     *
     * @param tAireadModel
     * @param cfg
     * @return
     */
    private WmInModel getWmInModelByTairead(TAireadModel tAireadModel, TAicbqCfgModel cfg, String deviceType) {
        try {
            //获取上次可靠的值,服务侧没有上次值,暂不支持识别。如果调用app识别,返回的又没有可信度的标记 2023-03-15 guozhao
            //廉博约定:服务器用服务器的前值 2023-03-30 guozhao
            BigDecimal lastValue = null;
            Date lastTime = null;
            TServerReadModel lastServerRead = tServerReadService.findPreGoodModelByDeviceSn(tAireadModel);
            TaskReviewModel reviewModel = taskReviewService.findLastByDeviceSn(tAireadModel.getDeviceSn());
            if (lastServerRead != null) {//有上次服务侧识别值
                TAireadModel aireadModel = tAireadService.getTaireadByAireadId(lastServerRead.getAireadId());
                if (aireadModel == null) {//未匹配到读数表,除手工删除外,还可能移至临时表 2024-02-01 guozhao
                    TempAiread tempAiread = tempAireadService.getTempAireadByAireadId(lastServerRead.getAireadId());
                    if (tempAiread != null) {
                        aireadModel = new TAireadModel();
                        BeanUtils.copyProperties(tempAiread, aireadModel);
                    }
                }

                if (reviewModel != null && reviewModel.getReviewValue() != null) {//优先审核值
                    if (aireadModel != null) {
                        //判断时间是为了防止使用过早的审核值(初期审核,后来不审了)
                        if (reviewModel.getReviewTime().compareTo(aireadModel.getSampleTime()) > -1) {
                            lastValue = reviewModel.getReviewValue();
                            lastTime = reviewModel.getSampleTime();
                        } else {
                            lastValue = lastServerRead.getIdentValue();
                            //测试时可能会有aireadModel=null,服务与读数表对应不上,先取服务侧时间,差距不大 2024-01-31 guozhao
                            lastTime = aireadModel == null ? lastServerRead.getCreateTime() : aireadModel.getSampleTime();
                        }
                    } else {//无法比较时间,直接使用审核值(常规情况aireadModel不应该为空的)
                        lastValue = reviewModel.getReviewValue();
                        lastTime = reviewModel.getSampleTime();
                    }
                } else {
                    lastValue = lastServerRead.getIdentValue();
                    //测试时可能会有aireadModel=null,服务与读数表对应不上,先取服务侧时间,差距不大 2024-01-31 guozhao
                    lastTime = aireadModel == null ? lastServerRead.getCreateTime() : aireadModel.getSampleTime();
                }
            } else {//如果服务侧从未识别过,或识别是错误的,从审核记录中找最新的 2023-03-30 guozhao
                if (reviewModel != null) {
                    lastValue = reviewModel.getReviewValue();
                    lastTime = reviewModel.getSampleTime();
                } else {//即没有服务侧又没有审核值,这种情况先用上次MCU的,不然这个设备一直都没有服务侧值  2023-07-04 guozhao
                    TAireadModel aireadModel = tAireadService.findPreGoodModelByDeviceSn(tAireadModel);
                    if (aireadModel != null) {
                        lastValue = aireadModel.getResultValue();
                        lastTime = aireadModel.getSampleTime();
                    }
                }
            }

            //MCU或服务侧都没有可靠的上次值
            if (lastValue == null || lastTime == null) {
                //约定:燃气表当前没有可用前值时,传给服务侧0 2024-01-10 guozhao
                lastValue = BigDecimal.ZERO;
                lastTime = new Date();

            /*if (deviceType.equals(DeviceType.GAS_METER.getCode())) {
                lastValue = BigDecimal.ZERO;
                lastTime = new Date();
            } else {
                return null;
            }*/
            }

            String inImg = StringUtils.isNotEmpty(tAireadModel.getBigJpgUrl()) ? tAireadModel.getBigJpgUrl() : tAireadModel.getSmallJpgUrl();
            String dirDate = DateUtil.getCurrentTime("yyyy-MM");
            String outPath = env.getProperty(ResourceTag.UploadPathTag.getTag()) + "server/" + dirDate + "/" + tAireadModel.getDeviceSn() + "/";
            File outDir = new File(outPath);
            if (!outDir.exists()) {
                outDir.mkdirs();
            }

            WmInModel wmInModel = new WmInModel();
            wmInModel.setImagePath(convertSmallUrl(inImg));
            wmInModel.setOutputPath(outPath);
            wmInModel.setNumNumbers(cfg.getDigitNum());
            UnitType unit = UnitType.getElement(cfg.getUnitType());
            if (unit != null) {
                wmInModel.setNumberUnit(unit.getValue());
            }

            StationAccModel accModel = stationAccService.findStationAccByStationCode(tAireadModel.getStationCode());
            if (accModel != null) {
                if (StringUtils.isNotEmpty(accModel.getReviewFlag()) && accModel.getReviewFlag().equals("1")) {
                    wmInModel.setIsGenSlsSmallJPG(true);//需要审核的,才生成小图
                }
            }
            wmInModel.setMeterFontStyle(0);//新的识别服务约定,统一使用0 2023-07-04 guozhao
            if (deviceType.equals(DeviceType.WATER_METER.getCode())) {
                DnType dntype = DnType.getElement(cfg.getDnType());
                if (dntype != null) {
                    wmInModel.setMeterDiameter(dntype.getType());
                }
            } else {
                wmInModel.setIsGenSlsSmallJPG(false);//燃气表不生成小图
                GasMeterSpec spec = GasMeterSpec.findGasMeterSpecByQMax(cfg.getDnType());
                if (spec != null) {
                    wmInModel.setMeterDiameter(spec.getName());
                } else {//兼容燃气表安装后,APP配置的仍是水表口径
                    DnType dntype = DnType.getElement(cfg.getDnType());
                    if (dntype != null) {
                        wmInModel.setMeterDiameter(dntype.getType());
                    }
                }

                //处理燃气表服务器侧指定数字区域、方向、角度等配置(暂时限定燃气表了,其它类型设备还未涉及) 2024-01-31 guozhao
                setServerIdenCfg(cfg.getDeviceSn(), wmInModel);
            }

            wmInModel.setLastOkValue(lastValue.doubleValue());
            wmInModel.setLastOkValueTime(DateUtil.formateDate(lastTime, "yyyy-MM-dd HH:mm:ss"));
            return wmInModel;
        } catch (Exception ex) {
            log.error(ex.getMessage());
            ex.printStackTrace();
        }
        return null;
    }

    private WmInModel setServerIdenCfg(String deviceSn, WmInModel wmInModel) {
        TempServerIdenCfg serverIdenCfg = tempServerIdenCfgService.findTempServerIdenCfgByDeviceSn(deviceSn);
        if (serverIdenCfg == null) {
            return wmInModel;
        }

        //数字个数等MCU会返上来的配置,暂不使用服务侧的配置
        /*if(serverIdenCfg.getDigitNum()!=null){
            wmInModel.setNumberUnit(serverIdenCfg.getDigitNum());
        }*/

        //安装方向
        if (StringUtils.isNotEmpty(serverIdenCfg.getSetupDir())) {
            wmInModel.setIs_updown(Integer.valueOf(serverIdenCfg.getSetupDir()));
        }
        //数字区域
        if (StringUtils.isNotEmpty(serverIdenCfg.getEnableConfig()) && serverIdenCfg.getEnableConfig().equals("1")) {
            wmInModel.setIsUseConfig(true);
            wmInModel.setConfigRotateAngle(serverIdenCfg.getImageLeanangle() == null ? 333 : serverIdenCfg.getImageLeanangle());
            wmInModel.setConfigNumberRegion(0, serverIdenCfg.getImageNumregionXmin() == null ? 333 : serverIdenCfg.getImageNumregionXmin());
            wmInModel.setConfigNumberRegion(1, serverIdenCfg.getImageNumregionYmin() == null ? 333 : serverIdenCfg.getImageNumregionYmin());
            wmInModel.setConfigNumberRegion(2, serverIdenCfg.getImageNumregionXmax() == null ? 333 : serverIdenCfg.getImageNumregionXmax());
            wmInModel.setConfigNumberRegion(3, serverIdenCfg.getImageNumregionYmax() == null ? 333 : serverIdenCfg.getImageNumregionYmax());
        }
        return wmInModel;
    }

    /**
     * post方式提交AI识别的输入参数
     *
     * @param inModel
     * @return
     */
    private ResultMap postInParam(Object inModel, String imgPath, String deviceType) {
        String url = env.getProperty(ResourceTag.AiIdenserverUrlTag.getTag()) + getApiUrl(deviceType);
        //供免配置压力表和LED表测试,是另外的地址 2024-07-03 guozhao
        if (deviceType.equals(DeviceType.PRESSURE_GAGE.getCode()) || deviceType.equals(DeviceType.LED_METER.getCode())) {
            url = env.getProperty(ResourceTag.NoConfigServerUrlTag.getTag()) + getApiUrl(deviceType);
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        AiTokenModel tokenModel = getTocken();
        if (tokenModel == null || tokenModel.getAccess_token() == null) {
            return null;//未获取到Token,连接服务可能失败了
        }
        headers.add("Authorization", "Bearer" + tokenModel.getAccess_token());
        //String imgPath = inModel.getImagePath();
        File file = new File(imgPath);
        if (!file.exists() || file.isDirectory()) {
            AiIdenMap aiIdenMap = new AiIdenMap();
            aiIdenMap.setCode("IMGERR");

            if (deviceType.equals("3")) {
                log.warn("图片无效:" + JSON.toJSONString(inModel));
            }

            return null;                //图片不存在
        }
        FileSystemResource resource = new FileSystemResource(file);
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("file", resource);
        map.add("inJson", JSON.toJSONString(inModel));

        try {
            HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(map, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);

            if (response.getStatusCode() == HttpStatus.OK) {
                String body = response.getBody();
                log.info("服务侧识别结果:" + body);
                ResultMap aiIdenMap = JSON.parseObject(body, new TypeReference<>() {
                });
                return aiIdenMap;
            } else {

            }
        } catch (Exception ex) {
            log.warn("识别出错:" + JSON.toJSONString(inModel));
            log.error(ex.getMessage());
        }
        return null;
    }

    /**
     * 转换小图本地路径为Url
     *
     * @param url
     * @return
     */
    private String convertSmallUrl(String url) {
        if (url.contains("http")) {
            int index = url.indexOf("//");
            if (index > -1) {
                url = url.substring(index + 2);
                index = url.indexOf("/");
                url = url.substring(index);
                String tar = env.getProperty(ResourceTag.UploadPathTag.getTag());
                url = url.replaceAll(ResourcePath.Image.getPath(), tar);
                return url;
            }
        }
        return "";
    }

    /**
     * 解析读数表中的坐标信息(预留)
     *
     * @param info
     */
    private void setImageCoordinateInfo(String info) {
        int flag = Integer.valueOf(info.substring(0, 2));
        int xmin = Integer.valueOf(info.substring(4, 8));
        int ymin = Integer.valueOf(info.substring(8, 12));
        int xmax = Integer.valueOf(info.substring(12, 16));
        int ymax = Integer.valueOf(info.substring(16));
    }

    /**
     * 获取Token(超30分钟自动刷新)
     *
     * @return
     */
    private AiTokenModel getTocken() {
        if (aiTokenModel != null) {
            Date now = new Date();
            long span = now.getTime() - aiTokenModel.getAccess_time().getTime();
            if (span / 1000 < api_token_validity) {
                return aiTokenModel;
            }
        }

        String url = env.getProperty(ResourceTag.AiIdenserverUrlTag.getTag()) + URL_AUTH_LOGIN;
        try {
            ResponseEntity<String> entity = restTemplate.postForEntity(url, "", String.class);
            if (entity.getStatusCode() == HttpStatus.OK) {
                String body = entity.getBody();
                ResultMap map = JSON.parseObject(body, new TypeReference<ResultMap<String>>() {
                });
                if (map.getFlag().equals("1")) {
                    aiTokenModel = new AiTokenModel();
                    aiTokenModel.setAccess_token(map.getRes().toString());
                    aiTokenModel.setAccess_time(new Date());
                } else {
                    System.out.println(map.getRes());
                }
            } else {
                System.out.println(entity.getStatusCode());
            }
        } catch (Exception ex) {//连接异常时才会发生
            log.error(ex.getMessage());
        }
        return aiTokenModel;
    }

    /**
     * 根据设备类型获取Api路径
     *
     * @param deviceType
     * @return
     */
    private String getApiUrl(String deviceType) {
        if (deviceType.equals(DeviceType.WATER_METER.getCode()) || deviceType.equals(DeviceType.GAS_METER.getCode()))//水表和煤气表
            return URL_WM_IDEN;
        else if (deviceType.equals(DeviceType.PRESSURE_GAGE.getCode())) {//压力表
            return URL_PG_IDEN;
        } else if (deviceType.equals(DeviceType.LED_METER.getCode())) {//液晶表
            return URL_LED_IDEN;
        } else
            return URL_WM_IDEN;
    }

    //测试使用
    public void getIdenResult() {
        WmInModel wmInModel = new WmInModel();
        wmInModel.setImagePath("D:/images/06300200000008_train.BMP");
        wmInModel.setOutputPath("D:/output_images/");
        wmInModel.setNumNumbers(6);
        wmInModel.setNumberUnit(1);
        wmInModel.setMeterFontStyle(0);
        wmInModel.setMeterDiameter("DN15");
        wmInModel.setLastOkValue(2.0);
        wmInModel.setLastOkValueTime("2022-12-2 13:33:00");
        wmInModel.setIsGenSlsSmallJPG(true);

        String url = env.getProperty(ResourceTag.AiIdenserverUrlTag.getTag()) + URL_WM_IDEN;
        String result = HttpUtil.postData(url, wmInModel);
        ResultMap<WmOutModel> resultMap = JSON.parseObject(result, new TypeReference<ResultMap<WmOutModel>>() {
        });
        System.out.println(resultMap.getRes());
    }

    /**
     * 根据服务侧配置的安装方向,旋转图片
     *
     * @param imgPath
     * @param setupDir
     */
    private void rotatingImage(String imgPath, String setupDir) {
        if (StringUtils.isEmpty(setupDir) || setupDir.equals("0")) return;//正向不处理

        int angel = 0;
        switch (setupDir) {
            case "1":
                angel = 180;
                break;
            case "2":
                angel = 90;
                break;
            case "3":
                angel = -90;
                break;
        }
        if (angel != 0) {
            ImageUtil.rotatingImage(imgPath, angel);
        }
    }

    /*private ResultMap testMethod(TAireadModel aireadModel, TAicbqCfgModel cfgModel, String imgPath){
        String url = "http://localhost:6070/FreeAiAction/getSampleResult";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        File file = new File(imgPath);
        if (!file.exists() || file.isDirectory()) {
            AiIdenMap aiIdenMap = new AiIdenMap();
            aiIdenMap.setCode("IMGERR");
            return null;                //图片不存在
        }
        FileSystemResource resource = new FileSystemResource(file);
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();

        FaSampleInfo sampleInfo = new FaSampleInfo(aireadModel);
        sampleInfo.setImgDataSize((int) file.length());

        map.add("file", resource);
        map.add("configModelJson", JSON.toJSONString(cfgModel));
        map.add("sampleInfoJson", JSON.toJSONString(sampleInfo));

        try {
            HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(map, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);

            if (response.getStatusCode() == HttpStatus.OK) {
                String body = response.getBody();
                log.info("服务侧识别结果:" + body);
                ResultMap<FaSampleResult> aiIdenMap = JSON.parseObject(body, new TypeReference<>() {
                });
                return aiIdenMap;
            } else {

            }
        } catch (Exception ex) {
            log.error(ex.getMessage());
        }
        return null;
    }*/
}
