package com.tofly.scada.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haiqiu.tools.excel.utils.ExcelUtils;
import com.tofly.common.constant.CalendarHelper;
import com.tofly.common.core.util.StringUtil;
import com.tofly.common.oauth.exception.ToflyDeniedException;
import com.tofly.em.IndexTypeInfoEnum;
import com.tofly.scada.common.FlowConstant;
import com.tofly.scada.common.MessageTopicEnum;
import com.tofly.scada.entity.*;
import com.tofly.scada.entity.dto.CustomDisplayDto;
import com.tofly.scada.entity.dto.DeviceQuery;
import com.tofly.scada.entity.dto.ScadaQuery;
import com.tofly.scada.entity.vo.*;
import com.tofly.scada.handle.ScadaHandler;
import com.tofly.scada.mapper.AllocationMapper;
import com.tofly.scada.mapper.DeviceArchiveManageMapper;
import com.tofly.scada.mapper.ScadaMapper;
import com.tofly.scada.mapper.ScadaReportMapper;
import com.tofly.scada.service.*;
import com.tofly.scada.util.SendTidingsUtil;
import com.tofly.scada.util.SnowFlake;
import com.tofly.scada.util.TimeUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author HaiQiu
 * @date 2022/4/25
 */
@Service
@Slf4j
public class ScadaServiceImpl extends ServiceImpl<ScadaMapper, Scada> implements ScadaService {

    public static final String SCADA_HISTORY = "SCADA_HISTORY";
    private static final Pattern pattern = Pattern.compile("-?[0-9]+.?[0-9]*");
    private final Logger logger = LoggerFactory.getLogger(ScadaServiceImpl.class);
    private static final String appId = "2410264259";
    private static final String privateKey = "086e6fe01364402e96fb34f7b7cb4a19";
    private static final String urlPrefix = "https://water.huizhong.co/";
    /**
     * 统计量放行记录的code
     */
//    @Value("${statisticsScadaNew.code}")
    private List<String> llj = new ArrayList<>();
    @Value("${waterSupplyAll.codes:EG.SHABA.JLJ}")
    private String waterSupplyAll;
    @Autowired
    private ScadaMapper scadaMapper;

    @Autowired
    private StatisticsScadaService statisticsScadaService;

    @Autowired
    private AllocationMapper allocationMapper;

    @Autowired
    private DeviceArchiveManageMapper deviceArchiveManageMapper;

    @Autowired
    private ScadaReportMapper reportMapper;

    @Autowired
    private SendTidingsUtil sendTidingsUtil;

    @Autowired
    private ScadaReportServiceImpl scadaReportService;

    @Autowired
    private TfScadaReportListService tfScadaReportListService;

    @Autowired
    private AllocationService allocationService;

    @Autowired
    private DmatableService dmatableService;

    @Autowired
    private ScadaMonitorService scadaMonitorService;


    @Autowired
    private DeviceArchiveManageService deviceArchiveManageService;

    @Autowired
    private TfScadaReportDealService tfScadaReportDealService;

    @Autowired
    private ScadaHandler scadaHandler;

    @Autowired
    private RxsmsmessageService rxsmsmessageService;

    /**
     * 获取日期列表
     *
     * @param start 开始时间:2022-01-01 00:00:00
     * @param end   结束时间:2022-10-01 14:00:00
     * @param type  统计类型 //统计类型0小时，1按天，2月份，3年份
     * @return
     */
    public static List<Date> findDates(Date start, Date end, Integer type) {
        Calendar s = Calendar.getInstance();
        s.setTime(start);
//        Calendar e = Calendar.getInstance();
//        e.setTime(end);
//        if (start.getYear() == end.getYear() && start.getMonth() == end.getMonth() && start.getDay() == end.getDay()) {
//            List<Date> dates = new ArrayList<>();
//            dates.add(start);
//            return dates;
//        }
//        if (start.getYear() == end.getYear() && start.getMonth() == end.getMonth() && type == 0) {
//            List<Date> dates = new ArrayList<>();
//            dates.add(start);
//            return dates;
//        }
        //结束时间在开始时间之后
        List<Date> dates = new ArrayList<>();
        dates.add(start);
        dates.add(end);
        while (end.getTime() > s.getTime().getTime()) {
            if (type == 1) {
                s.add(Calendar.DAY_OF_MONTH, 1);
            } else if (type == 2) {
                s.add(Calendar.MONTH, 1);
            } else if (type == 3) {
                s.add(Calendar.DAY_OF_YEAR, 1);
            } else if (type == 0) {
                s.add(Calendar.HOUR_OF_DAY, 1);
            } else if (type == 4) {
                s.add(Calendar.MINUTE, 5);
            }
            dates.add(s.getTime());
        }
        return dates;
    }

    /**
     * 获取报警ID集合(去重)
     *
     * @param allocations
     * @return
     */
    public static Set<Long> getReportIdSets(List<Allocation> allocations) {
        Set<Long> reports = new HashSet<>();
        allocations.forEach(allocation -> {
            if (StringUtils.hasText(allocation.getReportId())) {
                String[] reportIds = allocation.getReportId().split(",");
                for (String reportId : reportIds) {
                    reports.add(Long.parseLong(reportId));
                }
            }

        });

        return reports;
    }

    public static Set<Long> getReportIdSets_Old(List<Allocation> allocations) {
        Set<Long> reports = allocations.stream().filter(allocation -> StringUtils.hasText(allocation.getReportId()))
                .map(allocation -> {
                    if (allocation.getReportId().contains(",")) {
                        String[] reportIds = allocation.getReportId().split(",");
                        for (String reportId : reportIds) {
                            return Long.parseLong(reportId);
                        }
                    } else {
                        return Long.parseLong(allocation.getReportId());
                    }
                    return null;
                }).collect(Collectors.toSet());
        return reports;
    }

    /**
     * 判断字符串内容是否为数字类型
     *
     * @param str
     * @return
     */
    public static boolean isNumber(String str) {
        Matcher m = pattern.matcher(str);
        return m.matches();
    }


    /**
     * 按照月同步指定日期的数据 xxl-job
     * @param date
     */

    /**
     * 根据当前时间动态自动创建月份表，创建完成之后返回表名
     * <p>
     * 往后往前推迟月份，1即创建下一月的表，-1表示创建上个月表
     *
     * @return 月份表名
     */
    //每月最后28日凌晨2点执行创建下个月的数据表
    //@Scheduled(cron = "0 0 2 28 * ? ")
    public void createMonths() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.MONTH, 1);
        SimpleDateFormat format = new SimpleDateFormat("yyyy_MM");
        String date = format.format(cal.getTime());
        String tableName = "SCADA_MONTH_" + date;
        logger.info("开始检测Scada月份表状态");
        if (scadaMapper.verifyTableMonthsIsExits(tableName) <= 0) {
            logger.info("Scada月份表状态：不存在，开始创建");
            scadaMapper.createTableMonths(tableName);
            logger.info("Scada月份表状态：创建完成");
        }
        logger.info("结束检测Scada月份表状态");
    }

    public String createMonths(Date time) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(time == null ? new Date() : time);
        SimpleDateFormat format = new SimpleDateFormat("yyyy_MM");
        String date = format.format(cal.getTime());
        String tableName = "SCADA_MONTH_" + date;
        logger.info("开始检测Scada月份表状态");
        if (scadaMapper.verifyTableMonthsIsExits(tableName) <= 0) {
            logger.info("Scada月份表状态：不存在，开始创建");
            scadaMapper.createTableMonths(tableName);
            logger.info("Scada月份表状态：创建完成");
        }
        logger.info("结束检测Scada月份表状态");
        return tableName;
    }

    /**
     * 同步24小时数据，支持自定义时间添加 xxl-job
     */
    public void insert24H() {
        logger.info("同步scada历史数据到24小时表：正在进行时间调整");
        //整个日历调整时间
        Calendar calendar = Calendar.getInstance();
        Date date = new Date();
        calendar.setTime(date);
        //延迟一个小时
        calendar.add(Calendar.HOUR_OF_DAY, -1);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();
        String start = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        //拉长时间到59分59秒
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        Date endTime = calendar.getTime();
        String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        logger.info("同步scada历史数据到24小时表：正在进行查询历史数据");
        syncDataComm(null, startTime, endTime, FlowConstant.INTEGER_ZERO);

        logger.info("同步scada历史数据到24小时表结束");
    }


//    @Scheduled(cron = "0 0/2 * * * ? ")
//    @Transactional(rollbackFor = Exception.class)
//    public void automaticSendNetCumulativeFlow() {
//        Calendar calendar = Calendar.getInstance();
//        calendar.setTime(new Date());
//        calendar.add(Calendar.MINUTE, -1);
//        calendar.set(Calendar.SECOND, 59);
//        Date endTime = calendar.getTime();
//        calendar.add(Calendar.MINUTE, -1);
//        calendar.set(Calendar.SECOND, 0);
//        Date startTime = calendar.getTime();
//        sendNetCumulativeFlow(startTime, endTime);
//    }

    /**
     * 按照天同步数据 xxl-job
     *
     * @param date
     */
    public void syncDaysData(String date, String codes) {
        logger.info("同步scada历史数据到表：正在进行时间调整");
        //整个日历调整时间
        Date now = null;
        if (StringUtil.isNotNull(date)) {
            now = CalendarHelper.ConvertStrToDate(date, "yyyy-MM-dd");
        } else {
            now = new Date();
        }
        Calendar calendar = Calendar.getInstance();

        calendar.setTime(now);
        //延迟一天
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();

        //拉长时间到59分59秒
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        Date endTime = calendar.getTime();

        syncDataComm(codes, startTime, endTime, FlowConstant.INTEGER_ONE);

        //水厂供水量单独计算
        //水厂数据单独计算同步
        //syncDaysWaterCodes(waterSupplyAll,startTime,1);

        // 获取对应时间范围内数据参数


    }

    /**
     * 每月1号凌晨1点同步更新历史数据与上个月表数据，支持自定义时间添加
     */
    public void syncDataIntoMonths(String date) {
        logger.info("同步scada历史数据到月份表：正在进行时间调整");
        //整个日历调整时间
        Date now = null;
        if (StringUtil.isNotNull(date)) {
            now = CalendarHelper.ConvertStrToDate(date, "yyyy-MM-dd");
        } else {
            now = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        //倒退一个月开始时间
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();

        //倒退一个月最后一天时间
        calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        Date endTime = calendar.getTime();
        String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        logger.info("同步scada历史数据到月份表：正在进行历史数据查询....");
        syncDataComm(null, startTime, endTime, FlowConstant.INTEGER_TWO);

    }

    /**
     * 按照年同步数据 xxl-job
     *
     * @param year
     */

    public void syncDataIntoYears(Integer year) {
        logger.info("同步scada历史数据到年表：正在进行时间调整");
        //整个日历调整时间

        Calendar calendar = Calendar.getInstance();
        if (year != null && year > 0) {
            calendar.set(Calendar.YEAR, year);
        }
        calendar.set(Calendar.MONTH, 0);
        calendar.set(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date startTime = calendar.getTime();

        //倒退一个月最后一天时间
        calendar.set(Calendar.MONTH, 11);
        calendar.set(Calendar.DATE, 31);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        Date endTime = calendar.getTime();
        String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        logger.info("同步scada历史数据到年表：正在进行历史数据查询....");
        syncDataComm(null, startTime, endTime, FlowConstant.INTEGER_THREE);


    }

    /**
     * 定时任务，每分钟1执行一次 xxl-job
     */


    public void timingInsertData() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        Date end = calendar.getTime();
        calendar.add(Calendar.MINUTE, -5);
        Date start = calendar.getTime();

//        List<String> codes = getCodes(null);
//        if (CollectionUtils.isEmpty(codes)) {
//            return;
//        }
        List<Scada> scadas = scadaMapper.selectList(new QueryWrapper<Scada>().lambda().between(Scada::getUpdateTime, start, end));
        if (!CollectionUtils.isEmpty(scadas)) {
            //存储到分表月份
//            String tableName = createMonths(scadas.get(0).getScadaTime() == null ? new Date() : scadas.get(0).getScadaTime());
//            int i = insertMouthDataBatch(scadas, tableName);
//            log.info("{}新增合计数据：{}条", tableName, i);
            //监控+报警
            for (Scada scada : scadas) {
                // todo 暂时不使用 实时监控表
                if (scada.getValue().startsWith(".")) {
                    scada.setValue("0" + scada.getValue());
                }
//                Scada monitoring = scadaMapper.selectDataByMonitoring(scada.getCode());
//                log.info("查询存在的实时数据{}", monitoring);
//                if (ObjectUtils.isEmpty(monitoring)) {
//                    scadaMapper.insertMonitoring(scada);
//                    log.info("无实时数据，进行新增{}", scada);
//                } else {
//                    scadaMapper.updateMonitoring(scada);
//                    log.info("有实时数据，进行更新{}", scada);
//                }
                //检测报警信息
//                sendNotice(scada);
            }
        }
    }

    /**
     * 同步数据
     *
     * @param code，指定的编码集合
     * @param startTime    开始时间，精确到秒
     * @param endTime      结束时间，精确到秒
     * @param type         类型，0小时，1天，2月，3年
     */

    public void syncDataComm(String code, Date startTime, Date endTime, Integer type) {
        List<String> codesAll;
        logger.info("同步scada历史数据到表：正在进行时间调整");
        if (StringUtil.isNotEmpty(code)) {
            codesAll = Arrays.asList(code.split(","));

        } else {
            codesAll = getCodes(null);
        }

        if (codesAll.size() > 0) {

            int maxSize = 1000;
            int limit = codesAll.size() / maxSize;
            for (int i = 0; i <= limit; i++) {
                int NowSize = (i + 1) * maxSize;
                NowSize = NowSize > codesAll.size() ? codesAll.size() : NowSize;
                List<String> codes = codesAll.subList(i * maxSize, NowSize);
                List<StatisticsScadaCalVo> scadaLists = scadaMapper.selectCodesComm(codes, startTime, endTime, type);
                calRunTime(codes, startTime, endTime, type, scadaLists);
                // 删除旧数据
                statisticsScadaService.delete(type, codes, startTime, endTime);
                //保存数据
                saveStatisticsCode(scadaLists, type, true);

            }

        }


    }

    /**
     * 分code进行多线程存储
     *
     * @param dateType 统计类型
     * @param isAuto   是否为自动定时任务，true定时任务，false不是定时任务
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveStatisticsCode(List<StatisticsScadaCalVo> scadaLists, Integer dateType, boolean isAuto) {
        if (!CollectionUtils.isEmpty(scadaLists)) {
            logger.info("计算scada统计数据：正在进行数据大小{}", scadaLists.size());
            // 存储新增统计数据
            List<StatisticsScada> saveList = new ArrayList<>();
            if (CollectionUtils.isEmpty(scadaLists)) {
                return;
            }
            scadaLists.forEach(k -> {
                StatisticsScada statisticsScada = new StatisticsScada();
                statisticsScada.setCode(k.getCode());
                String maxValue = k.getMaxValue() == null ? "0" : new BigDecimal(k.getMaxValue()) + "";
                statisticsScada.setValue(maxValue);
                statisticsScada.setMin(k.getMinValue() == null ? "0" : new BigDecimal(k.getMinValue()) + "");
                statisticsScada.setMax(maxValue);
                List<Date> dates = getDateListByType(getDateType(k.getDateTimeStr(), dateType, k.getCode()), dateType);
                if (!CollectionUtils.isEmpty(dates) && dates.size() == 2) {
                    statisticsScada.setStartTime(dates.get(0));
                    statisticsScada.setEndTime(dates.get(1));
                }
                DecimalFormat decimalFormat = new DecimalFormat("#0.00");//保留2位小数
                //取一段时间内的平均值
                try {
                    BigDecimal avg = scadaMapper.getHistoryAvgValueByScadaTime(statisticsScada.getStartTime(), statisticsScada.getEndTime(), statisticsScada.getCode());

                    statisticsScada.setAve(decimalFormat.format(avg));
                } catch (Exception e) {

                }
                statisticsScada.setTimeTotal(k.getRunTime());
                statisticsScada.setErrorTimeTotal(k.getErrTime());
                statisticsScada.setErrorCount("0");
                if (statisticsScada.getStartTime() != null) { //取头天最大的值作为第二天的最小值
                    Scada scadaCheck = baseMapper.selectLastOne(k.getCode(), statisticsScada.getStartTime());

                    if (scadaCheck != null) {
                        statisticsScada.setMin(scadaCheck.getValue());
                    }

                }

                BigDecimal fold = new BigDecimal(statisticsScada.getMax()).subtract(new BigDecimal(statisticsScada.getMin()));

                statisticsScada.setFold(decimalFormat.format(fold));

                statisticsScada.setStatisticsType(String.valueOf(dateType));
                statisticsScada.setDataType(k.getAllocationType());
                //添加数据
                saveList.add(statisticsScada);

            });
            // 新增
            logger.info("新增scada数据大小{}", saveList.size());
            statisticsScadaService.saveBatch(saveList);


        } else {
            logger.info("获取历史数据为空");
        }
    }

    private Date getDateType(String timeStr, Integer type, String code) {
        try {
            if (0 == type) {
                String s = timeStr + ":00:00";
                LocalDateTime localDateTime = TimeUtils.fromString2LocalDateTime(s, "yyyy-MM-dd HH:mm:ss");
                return TimeUtils.convertLDTToDate(localDateTime);
            } else if (1 == type) {
                return TimeUtils.localDateToDate(TimeUtils.fromString2LocalDate(timeStr, "yyyy-MM-dd"));
            } else if (2 == type) {
                String s = timeStr + "-01";
                return TimeUtils.localDateToDate(TimeUtils.fromString2LocalDate(s, "yyyy-MM-dd"));
            } else if (3 == type) {
                String s = timeStr + "-01-01";
                return TimeUtils.localDateToDate(TimeUtils.fromString2LocalDate(s, "yyyy-MM-dd"));
            }
        } catch (Exception e) {
            System.out.println(code);
        }
        return null;
    }

    /**
     * 获取时间的小时，天，月份的开始时间和结束时间
     *
     * @param time 时间
     * @param type 类型
     * @return
     */
    public List<Date> getDateListByType(Date time, Integer type) {
        //整个日历调整时间

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time == null ? new Date() : time);
        List<Date> dates = new ArrayList<>();
        try {
            if (type.equals(FlowConstant.INTEGER_ZERO)) {
                //一个小时
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                Date startTime = calendar.getTime();
                String start = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
                //拉长时间到59分59秒
                calendar.set(Calendar.MINUTE, 59);
                calendar.set(Calendar.SECOND, 59);
                calendar.set(Calendar.MILLISECOND, 999);
                Date endTime = calendar.getTime();
                String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
                dates.add(startTime);
                dates.add(endTime);
            } else if (type.equals(FlowConstant.INTEGER_ONE)) {
                //一天
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                Date startTime = calendar.getTime();
                String start = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
                //拉长时间到59分59秒
                calendar.set(Calendar.HOUR_OF_DAY, 23);
                calendar.set(Calendar.MINUTE, 59);
                calendar.set(Calendar.SECOND, 59);
                calendar.set(Calendar.MILLISECOND, 999);
                Date endTime = calendar.getTime();
                String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
                dates.add(startTime);
                dates.add(endTime);
            } else if (type.equals(FlowConstant.INTEGER_TWO)) {
                //一个月
                calendar.set(Calendar.DATE, 1);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                Date startTime = calendar.getTime();
                String start = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
                //倒退一个月最后一天时间
                calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
                calendar.set(Calendar.HOUR_OF_DAY, 23);
                calendar.set(Calendar.MINUTE, 59);
                calendar.set(Calendar.SECOND, 59);
                calendar.set(Calendar.MILLISECOND, 999);
                Date endTime = calendar.getTime();
                dates.add(startTime);
                dates.add(endTime);

            } else if (type.equals(FlowConstant.INTEGER_THREE)) {
                //一个月
                calendar.set(Calendar.DATE, 1);
                calendar.set(Calendar.MONTH, 0);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                Date startTime = calendar.getTime();
                String start = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
                //倒退一个月最后一天时间
                calendar.set(Calendar.DATE, 31);
                calendar.set(Calendar.MONTH, 11);
                calendar.set(Calendar.HOUR_OF_DAY, 23);
                calendar.set(Calendar.MINUTE, 59);
                calendar.set(Calendar.SECOND, 59);
                calendar.set(Calendar.MILLISECOND, 999);
                Date endTime = calendar.getTime();
                dates.add(startTime);
                dates.add(endTime);

            }
        } catch (Exception e) {

        }
        return dates;
    }

    /**
     * 获取历史数据
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 历史数据
     */
    public List<Scada> getScadasDatas(String code, Date start, Date end) {
        List<Allocation> allocations = allocationMapper.selectList(Wrappers.<Allocation>lambdaQuery()
                .eq(StringUtils.hasText(code), Allocation::getVariableCode, code));
        List<String> codes = allocations.stream().filter(allocation -> allocation.getVariableCode() != null)
                .map(Allocation::getVariableCode).collect(Collectors.toList());

        List<Scada> scadas = new ArrayList<>();
        if (!CollectionUtils.isEmpty(codes)) {
            QueryWrapper<Scada> scadaQueryWrapper = new QueryWrapper<>();
            scadaQueryWrapper.between("SCADA_TIME", start, end)
                    .in("CODE", codes);
            scadas = scadaMapper.selectList(scadaQueryWrapper);
        }
        return scadas;
    }

    public List<String> getCodes(String code) {
        if (StringUtils.hasText(code) && code.contains(",")) {
            String[] codeList = code.split(",");
            List<Allocation> allocations = allocationMapper.selectList(Wrappers.<Allocation>lambdaQuery()
                    .in(!CollectionUtils.isEmpty(Arrays.asList(codeList)), Allocation::getVariableCode, codeList));
            List<String> codes = allocations.stream().filter(allocation -> allocation.getVariableCode() != null)
                    .map(Allocation::getVariableCode).collect(Collectors.toList());

            if (!CollectionUtils.isEmpty(codes)) {
                return codes;
            }
        }
        List<Allocation> allocations = allocationMapper.selectList(Wrappers.<Allocation>lambdaQuery()
                .eq(StringUtils.hasText(code), Allocation::getVariableCode, code));
        List<String> codes = allocations.stream().filter(allocation -> allocation.getVariableCode() != null)
                .map(Allocation::getVariableCode).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(codes)) {
            return codes;
        }
        return null;
    }

    @Override
    public List<Scada> getListData() {
        List<Scada> scadaList = scadaMapper.selectMonitoring("SCADA_MONITORING");
        for (Scada scada : scadaList) {
            if (StringUtils.hasText(scada.getValue()) && scada.getValue().startsWith(".")) {
                scada.setValue("0" + scada.getValue());
            }
        }
        return scadaList;
    }

    @Override
    public List<ScadaVo> selectMonitoringNew(String codes) {
        List<String> listCodes = new ArrayList<>();
        if (StringUtil.isNotEmpty(codes)) {
            listCodes = Arrays.asList(codes.split(","));

        }
        List<ScadaVo> scadaList = scadaMapper.selectMonitoringNew(listCodes);
        for (ScadaVo scada : scadaList) {
            if (StringUtils.hasText(scada.getValue()) && scada.getValue().startsWith(".")) {
                scada.setValue("0" + scada.getValue());
            }
        }
        return scadaList;
    }

    @Override
    public List<DeviceArchiveManageVo> getAppData(DeviceQuery deviceQuery) {
        QueryWrapper<DeviceArchiveManage> queryWrapper = getQueryWrapper(deviceQuery);
        List<DeviceArchiveManage> deviceArchiveManages = deviceArchiveManageMapper.pageList(queryWrapper);
        if (CollectionUtils.isEmpty(deviceArchiveManages)) {
            return null;
        }

        //查询所有指标
        List<Allocation> allocations = allocationMapper.selectList(Wrappers.<Allocation>lambdaQuery()
                .eq(Allocation::getIsDisplay, 1)
                .orderByAsc(Allocation::getDeviceId).orderByAsc(Allocation::getSortNumber));
        //查询所有报警
        List<ScadaReport> scadaReports = reportMapper.selectList(Wrappers.<ScadaReport>lambdaQuery()
                .eq(ScadaReport::getIsDel, 0));
        //查询最新scada数据
        List<Scada> scadas = getListData();

        List<DeviceArchiveManageVo> manageVos = deviceArchiveManages.stream().map(deviceArchiveManage -> {
            DeviceArchiveManageVo vo = new DeviceArchiveManageVo();
            BeanUtils.copyProperties(deviceArchiveManage, vo);
            vo.setIsAlarm("0");
            //查询绑定的指标
            if (!CollectionUtils.isEmpty(allocations)) {
                //获取符合设备绑定的指标
                List<AllocationVo> allocationVos = new HashSet<>(allocations).stream().filter(allocation -> allocation.getDeviceId() != null &&
                        allocation.getDeviceId().equals(vo.getId())).map(allocation -> {
                    AllocationVo allocationVo = new AllocationVo();
                    BeanUtils.copyProperties(allocation, allocationVo);
                    Scada scadaBelong = null;
                    //查询scada数据

                    if (!CollectionUtils.isEmpty(scadas)) {
                        List<Scada> scadaList = scadas.stream()
                                .filter(scada -> scada.getCode() != null && scada.getCode().equals(allocation.getVariableCode()))
                                .collect(Collectors.toList());
                        if (!CollectionUtils.isEmpty(scadaList)) {
                            scadaBelong = scadaList.get(0);
                            Long accuracy = allocation.getAccuracy();
                            //根据设置的精度保留小数位数
                            if (accuracy == null) {
                                accuracy = 2L;
                            }
                            //科学技术法处理，不要e哦
                            BigDecimal bd = new BigDecimal(scadaBelong.getValue());
                            double roundedNumber = bd.setScale(Integer.parseInt(accuracy + ""), BigDecimal.ROUND_HALF_UP).doubleValue();
                            NumberFormat nf = NumberFormat.getInstance();
                            nf.setGroupingUsed(false);

                            if (accuracy > 0L) {
                                scadaBelong.setValue(nf.format(roundedNumber));
                            } else {
                                scadaBelong.setValue(nf.format((int) roundedNumber));
                            }

                            allocationVo.setScada(scadaBelong);
                        }
                    }
                    allocationVo.setIsAlarm("0");
                    //查询是否展示报警
                    if (org.apache.commons.lang3.StringUtils.isBlank(allocationVo.getIsAlarm())
                            || allocationVo.getIsAlarm().equals("0")) {
                        return allocationVo;
                    }
                    //设置报警相关信息
                    Set<Long> reportIdSets = getReportIdSets(Collections.singletonList(allocation));
                    if (!CollectionUtils.isEmpty(scadaReports) && !CollectionUtils.isEmpty(reportIdSets)) {
                        //筛选出报警信息列表
                        List<ScadaReport> reportsList = new ArrayList<>();
                        List<ScadaReport> reports = scadaReports.stream().filter(scadaReport -> scadaReport.getId() != null
                                && reportIdSets.contains(scadaReport.getId())).collect(Collectors.toList());
                        //判断指标是否报警
                        if (scadaBelong != null && scadaBelong.getValue() != null) {
                            for (ScadaReport scadaReport : reports) {
                                if (scadaReport.getReportLower() == null || scadaReport.getReportUpper() == null) {
                                    continue;
                                }
                                if (!isNumber(scadaReport.getReportLower())
                                        || !isNumber(scadaReport.getReportUpper())
                                        || !isNumber(scadaBelong.getValue())) {
                                    continue;
                                }
                                Double lower = Double.parseDouble(scadaReport.getReportLower());
                                Double upper = Double.parseDouble(scadaReport.getReportUpper());
                                Double value = Double.parseDouble(scadaBelong.getValue());

                                if (value.compareTo(upper) <= 0 && value.compareTo(lower) >= 0) {
                                    allocationVo.setIsAlarm("1");
                                    vo.setIsAlarm("1");
                                    reportsList.add(scadaReport);
                                    // break;
                                }

                            }
                        }
                        allocationVo.setScadaReports(reportsList);
                    }
                    return allocationVo;
                }).collect(Collectors.toList());


                allocationVos = allocationVos.stream().sorted(Comparator.comparing(AllocationVo::getSortNumber)).collect(Collectors.toList());

                vo.setAllocations(allocationVos);
            }
            if (org.apache.commons.lang3.StringUtils.isNotBlank(deviceQuery.getDeviceTypeIds())) {
                List<String> deviceTypeList = Arrays.stream(deviceQuery.getDeviceTypeIds().split(",")).map(String::trim)
                        .collect(Collectors.toList());
                for (String s : deviceTypeList) {
                    if (StringUtil.isNotEmpty(vo.getDeviceType()) && vo.getDeviceType().equals(s)) {
                        return vo;
                    }
                }
                return null;
            }
            return vo;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        return manageVos;
    }

    /**
     * 设置查询条件
     *
     * @param deviceQuery
     * @return
     */

    private QueryWrapper<DeviceArchiveManage> getQueryWrapper(DeviceQuery deviceQuery) {
        QueryWrapper<DeviceArchiveManage> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNotNull("manage.NAME");
        queryWrapper.isNotNull("manage.TYPE");
        if (!ObjectUtils.isEmpty(deviceQuery)) {
            if (StringUtils.hasText(deviceQuery.getType())) {
                List<String> types = Arrays.asList(deviceQuery.getType().split(","));
                queryWrapper.in("manage.TYPE", types);
            }
            if (StringUtils.hasText(deviceQuery.getDeviceType())) {
                queryWrapper.eq("manage.DEVICE_TYPE", deviceQuery.getDeviceType());
            }
            if (StringUtil.isNotNull(deviceQuery.getWaterId())) {
                queryWrapper.eq("manage.WATER_ID", deviceQuery.getWaterId());
            }
            if (StringUtils.hasText(deviceQuery.getStatus())) {
                queryWrapper.eq("manage.STATUS", deviceQuery.getStatus());
            }
            if (StringUtils.hasText(deviceQuery.getQuery())) {
                queryWrapper.and(q -> {
                    q.like("manage.ADDRESS", deviceQuery.getQuery())
                            .or().like("manage.NAME", deviceQuery.getQuery())
                            .or().like("manage.CODE", deviceQuery.getQuery())
                            .or().like("manage.DESCRIBE", deviceQuery.getQuery())
                            .or().like("allocation.VARIABLE_CODE", deviceQuery.getQuery())
                            .or().like("allocation.VARIABLE_NAME", deviceQuery.getQuery())
                            .or().like("allocation.DISPLAY_NAME", deviceQuery.getQuery());
                });

            }
            if (StringUtils.hasText(deviceQuery.getName())) {
                queryWrapper.like("manage.NAME", deviceQuery.getName());
            }
            if (StringUtils.hasText(deviceQuery.getCode())) {
                queryWrapper.like("allocation.VARIABLE_CODE", deviceQuery.getCode());
            }
            if (deviceQuery.getDeviceId() != null) {
                queryWrapper.eq("manage.ID", deviceQuery.getDeviceId());
            }
            if (!CollectionUtils.isEmpty(deviceQuery.getDeviceIds())) {
                queryWrapper.in("manage.ID", deviceQuery.getDeviceIds());
            }
            if (deviceQuery.getLatitude() != null && deviceQuery.getLongitude() != null) {
                queryWrapper.eq("manage.LATITUDE", deviceQuery.getLatitude())
                        .eq("manage.LONGITUDE", deviceQuery.getLongitude());
            }

            if (StringUtil.isNotNull(deviceQuery.getWaterType())) {
                queryWrapper.eq("water.WATER_TYPE", deviceQuery.getWaterType());
            }

        }
        return queryWrapper;
    }

    @Override
    public void insertData() {
        List<ScadaMonitor> scadaMonitorList = scadaMonitorService.list();
        for (ScadaMonitor scadaMonitor : scadaMonitorList) {
            Scada scada = new Scada();
            BeanUtils.copyProperties(scadaMonitor, scada);
            //检测报警信息
//            sendNotice(scada);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void fetchData(String code, String start, String end, Integer type) {
        Date startTime;
        Date endTime;
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            startTime = format.parse(start);
            endTime = format.parse(end);
        } catch (Exception e) {
            throw new RuntimeException("时间格式有误");
        }
        long s = System.currentTimeMillis();
        //统计类型0小时，1按天，2月份，3年份
        logger.info("同步scada历史数据开始");

        log.info("开始进行统计计算-----{}", type);

        syncDataComm(code, startTime, endTime, type);
        long e = System.currentTimeMillis();
        logger.info("统计计算小时结束,用时{}", e - s);

    }

    @Override
    public Page<StatisticsScadaVo> selectHistoryByMouthsTables(String mouthTable, Page page,
                                                               ScadaQuery scadaQuery, boolean isPage) {
        if (isPage) {
            //特殊值处理
//            defaultValHandle(statisticsScadaVoPage);
            return baseMapper.selectHistoryByMouthsTablePage(mouthTable, page, scadaQuery);
        } else {
            Page<StatisticsScadaVo> pageDb = new Page<>();
            List<StatisticsScadaVo> statisticsScadaVoPage = baseMapper.selectHistoryByMouthsTableList(mouthTable, scadaQuery);
            pageDb.setRecords(statisticsScadaVoPage);
            //特殊值处理
//            defaultValHandle(pageDb);
            return pageDb;
        }
    }

    /**
     * 发送指标报警信息
     *
     */
    @SneakyThrows
    @Async
    @Override
    public void sendIndexNotice() {
        log.info("======================开始发送指标报警信息=======================");
        List<Allocation> allocations = allocationService.list();
        List<DeviceArchiveManage> deviceArchiveManageList = deviceArchiveManageService.list();
        List<ScadaReport> scadaReportList = scadaReportService.list();
        List<ScadaMonitor> scadaMonitorList = scadaMonitorService.list();
        for (ScadaMonitor scadaMonitor : scadaMonitorList) {
            Scada scada = new Scada();
            BeanUtils.copyProperties(scadaMonitor, scada);
            if (!StringUtils.hasText(scada.getValue())) {
                continue;
            }
            List<Allocation> allocationsFinal = allocations.stream().filter(e -> e.getVariableCode().equals(scada.getCode())).collect(Collectors.toList());
            if (CollUtil.isEmpty(allocationsFinal)) {
                continue;
            }
            Allocation allocation = allocationsFinal.get(0);
            if (allocation.getDeviceId() == null) {
                continue;
            }
            if (org.apache.commons.lang3.StringUtils.isBlank(allocation.getIsAlarm()) || allocation.getIsAlarm().equals("0")) {
                continue;
            }
            DeviceArchiveManage deviceArchiveManage = deviceArchiveManageList.stream().filter(e -> e.getId().equals(allocation.getDeviceId())).collect(Collectors.toList()).get(0);
            List<Long> reports = getReportIds(Collections.singletonList(allocation));
            if (CollUtil.isEmpty(reports)) {
                continue;
            }
            List<ScadaReport> scadaReports = scadaReportList.stream().filter(e -> reports.contains(e.getId())).collect(Collectors.toList());
            BigDecimal scadaVal = new BigDecimal(scada.getValue());
            for (ScadaReport scadaReport : scadaReports) {
                BigDecimal upVal = new BigDecimal(scadaReport.getReportUpper());
                BigDecimal downVal = new BigDecimal(scadaReport.getReportLower());
                //当值不在正常的范围里，需要报警处理
                if (scadaVal.compareTo(upVal) > 0 || scadaVal.compareTo(downVal) < 0) {
                    //报警人处理
                    List<String> list_user = new ArrayList<>();
                    if (scadaReport.getLiaisonPeople() != null) {
                        list_user.add(String.valueOf(scadaReport.getLiaisonPeople()));
                    }
                    if (StringUtil.isNotEmpty(scadaReport.getNoticeUser())) {
                        List<String> noticeUser = Arrays.asList(scadaReport.getNoticeUser().split(","));
                        noticeUser.forEach(a -> {
                            if (!list_user.contains(a)) {
                                list_user.add(a);
                            }
                        });
                    }
                    String message = createMsg(scadaReport.getMsgTemplate(), scadaReport, allocation, deviceArchiveManage, scada);
                    //保存报警信息列表数据
                    TfScadaReportList tfScadaReportList = saveReportListData(scada, scadaReports, allocationsFinal, message);
                    if (tfScadaReportList != null) {
                        for (String user : list_user) {
                            if (StringUtil.isNotEmpty(user)) {
//                                TfScadaReportDeal tfScadaReportDeal = new TfScadaReportDeal();
//                                tfScadaReportDeal.setDealUserId(Long.parseLong(user));
//                                tfScadaReportDeal.setReportId(tfScadaReportList.getId());
//                                tfScadaReportDealService.save(tfScadaReportDeal);
                                MessagepushW messagepush = new MessagepushW();
                                messagepush.setTopic(MessageTopicEnum.REPORT_MSG.getTopic());
                                messagepush.setMessage(message);
                                messagepush.setType("9");
                                messagepush.setTableId(tfScadaReportList.getId());
                                messagepush.setTableName(MessageTopicEnum.REPORT_MSG.getTableName());
                                messagepush.setCreateTime(scada.getScadaTime());
                                sendTidingsUtil.sendMsg(messagepush, Long.parseLong(user));
                            }
                        }
                    }
                    //发送短信到负责人
                    Rxsmsmessage rxsmsmessage = new Rxsmsmessage();
                    rxsmsmessage.setCreateDate(new Date());
                    rxsmsmessage.setIslock("0");
                    rxsmsmessage.setFszt("0");
                    rxsmsmessage.setMobile(scadaReport.getLeaderPhone());
                    rxsmsmessage.setTxlx("10");
                    rxsmsmessage.setTxnr("【德运水投】" + createMsg(scadaReport.getMsgTemplate(), scadaReport, allocation, deviceArchiveManage, scada));
                    rxsmsmessageService.save(rxsmsmessage);
                }
            }
        }
        log.info("======================发送指标报警信息结束=======================");
    }

    private TfScadaReportList saveReportListData(Scada scada, List<ScadaReport> scadaReports, List<Allocation> allocations, String message) {
        TfScadaReportList tfScadaReportList = new TfScadaReportList();
        BeanUtils.copyProperties(scada, tfScadaReportList);
        List<String> levelTitles = scadaReports.stream().filter(scadaReport -> StringUtils.hasText(scadaReport.getReportLevel()))
                .map(ScadaReport::getReportLevel).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(levelTitles)) {
            // String join = org.apache.commons.lang3.StringUtils.join(levelTitles);
            String join = levelTitles.stream().map(String::valueOf).collect(Collectors.joining(","));

            tfScadaReportList.setReportLevel(join);
        }
        List<String> reportUppers = scadaReports.stream().filter(scadaReport -> StringUtils.hasText(scadaReport.getReportUpper()))
                .map(ScadaReport::getReportUpper).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(reportUppers)) {
            // String join = org.apache.commons.lang3.StringUtils.join(reportUppers);
            String join = reportUppers.stream().map(String::valueOf).collect(Collectors.joining(","));
            tfScadaReportList.setReportUpper(join);
        }
        List<String> reportLowers = scadaReports.stream().filter(scadaReport -> StringUtils.hasText(scadaReport.getReportLower()))
                .map(ScadaReport::getReportLower).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(reportLowers)) {
            String join = reportLowers.stream().map(String::valueOf).collect(Collectors.joining(","));
            // String join = org.apache.commons.lang3.StringUtils.join(reportLowers);
            tfScadaReportList.setReportLower(join);
        }
        List<String> displayNames = allocations.stream().filter(scadaReport -> StringUtils.hasText(scadaReport.getDisplayName()))
                .map(Allocation::getDisplayName).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(displayNames)) {
            String join = displayNames.stream().map(String::valueOf).collect(Collectors.joining(","));
            // String join = org.apache.commons.lang3.StringUtils.join(displayNames);
            tfScadaReportList.setDisplayName(join);
        }
        tfScadaReportList.setStatus("0");
        tfScadaReportList.setReportContent(message);
        int count = tfScadaReportListService.count(Wrappers.<TfScadaReportList>lambdaQuery()
                .eq(TfScadaReportList::getScadaTime, scada.getScadaTime()));

        if (count == 0) {
            tfScadaReportListService.save(tfScadaReportList);
            return tfScadaReportList;
        }
        log.info("添加列表报警数据成功：{}", tfScadaReportList);
        return null;

        // return tfScadaReportList;

    }

    /**
     * 构建信息内容模版
     *
     * @param template
     * @param scadaReport
     * @return
     */
    public String createMsg(String template, ScadaReport scadaReport, Allocation allocation, DeviceArchiveManage deviceArchiveManage, Scada scada) {
        //设备名称，设备编码，指标编码，指标名称，时间，上限、下限，采集值，超标
        String unit = allocation.getUnit();//单位
        if (template.contains("#TIME#")) {
            String date = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
            template = template.replaceAll("#TIME#", date);
        }
        if (template.contains("#YHXM#")) {
            String user = scadaReportService.getUser(scadaReport.getLiaisonPeople());
            template = template.replace("#YHXM#", user);
        }
        if (template.contains("#LXDH#")) {
            template = template.replace("#LXDH#", scadaReport.getLiaisonPhone());
        }

        //报警上限
        if (template.contains("#BJSX#")) {
            String BJSX = "";
            BJSX = scadaReport.getReportUpper() + unit;
            template = template.replace("#BJSX#", BJSX);
        }
        //报警下限
        if (template.contains("#BJXX#")) {
            String BJXX = "";
            BJXX = scadaReport.getReportLower() + unit;
            template = template.replace("#BJXX#", BJXX);
        }

        //设备名称
        if (template.contains("#SBMC#")) {
            String SBMC = "";
            if (deviceArchiveManage != null) {
                SBMC = deviceArchiveManage.getName();
            }
            template = template.replace("#SBMC#", SBMC);
        }
        //设备编码
        if (template.contains("#SBBM#")) {
            String SBBM = "";
            if (deviceArchiveManage != null) {
                SBBM = deviceArchiveManage.getCode();
            }
            template = template.replace("#SBBM#", SBBM);
        }
        //指标名称
        if (template.contains("#ZBMC#")) {
            String ZBMC = "";
            ZBMC = allocation.getDisplayName();
            template = template.replace("#ZBMC#", ZBMC);
        }
        //指标编码
        if (template.contains("#ZBBM#")) {
            String ZBBM = "";
            ZBBM = allocation.getVariableCode();
            template = template.replace("#ZBBM#", ZBBM);
        }

        //报警值
        if (template.contains("#BJZ#")) {
            String BJZ = "";
            BJZ = scada.getValue() + unit;

            template = template.replace("#BJZ#", BJZ);
        }
        //超标
        if (template.contains("#BJCB#")) {
            String BJCB = "";
            BigDecimal S_BJCB = new BigDecimal(scada.getValue());
            BigDecimal upper = new BigDecimal(scadaReport.getReportUpper());
            BigDecimal lower = new BigDecimal(scadaReport.getReportLower());
            if (S_BJCB.compareTo(upper) > 0) {
                BigDecimal add = S_BJCB.subtract(upper);
                BJCB = "上限：" + add + unit;
            }
            if (S_BJCB.compareTo(lower) < 0) {
                BigDecimal add = lower.subtract(S_BJCB);
                BJCB = "下限：" + add + unit;
            }

            template = template.replace("#BJCB#", BJCB);
        }

        return template;
    }

    /**
     * 获取报警ID集合
     *
     * @param allocations
     * @return
     */
    private List<Long> getReportIds(List<Allocation> allocations) {
        List<Long> reports = allocations.stream().filter(allocation -> StringUtils.hasText(allocation.getReportId()))
                .map(allocation -> {
                    if (allocation.getReportId().contains(",")) {
                        String[] reportIds = allocation.getReportId().split(",");
                        for (String reportId : reportIds) {
                            return Long.parseLong(reportId);
                        }
                    } else {
                        return Long.parseLong(allocation.getReportId());
                    }
                    return null;
                }).collect(Collectors.toList());
        return reports;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void manualSendNetCumulativeFlow(Date sDate, Date eDate) {
        log.info("手动同步scada正反数据");
        sendNetCumulativeFlow(sDate, eDate);

    }

    @Override
    public List<Map<String, Object>> viewScadaData(CustomDisplayDto dto) {
        if (org.apache.commons.lang3.StringUtils.isBlank(dto.getStartDate())
                || org.apache.commons.lang3.StringUtils.isBlank(dto.getStartDate())) {
            throw new ToflyDeniedException("未传入开始时间和结束时间");
        }
        dto.setIndexType(IndexTypeInfoEnum.CUM_FLOW.getId());
        List<StationIndexVo> stationList = this.baseMapper.viewScadaData(dto);

        if (org.apache.commons.lang3.StringUtils.isNotBlank(dto.getIsExport()) && dto.getIsExport().equals("1")) {
            ExcelUtils.getListToExcel(StationIndexVo.class, stationList, "报表");
        }
        if (CollUtil.isEmpty(stationList)) {
            return null;
        }
        List<Map<String, Object>> res = new ArrayList<>();
        if (dto.getDeviceIdList() == null || CollUtil.isEmpty(dto.getDeviceIdList())) {
            dto.setDeviceIdList(deviceArchiveManageService.list().stream().map(DeviceArchiveManage::getId).collect(Collectors.toList()));
        }
        List<String> dates = getDates(dto.getStartDate(), dto.getEndDate(), dto.getStatisticsType());
        dto.getDeviceIdList().forEach(deviceId -> {
            for (String date : dates) {
                Map<String, Object> map = new HashMap<>();
                map.put("deviceId", deviceId);
                map.put("deviceName", deviceArchiveManageService.getById(deviceId).getName());
                map.put("scadaTime", date);
                List<StationIndexVo> listFinal;
                switch (dto.getStatisticsType()) {
                    case 0:
                        listFinal = stationList.stream()
                                .filter(e -> Objects.equals(e.getDeviceId(), deviceId)
                                        && date.equals(DateUtil.format(DateUtil.parse(e.getScadaTime(), "yyyy-MM-dd HH:mm:ss"), "yyyy-MM-dd")))
                                .collect(Collectors.toList());
                        if (CollUtil.isEmpty(listFinal)) {
                            return;
                        }
                        map.put("dataList", listFinal);
                        break;
                    case 1:
                        listFinal = stationList.stream()
                                .filter(e -> Objects.equals(e.getDeviceId(), deviceId)
                                        && date.equals(DateUtil.format(DateUtil.parse(e.getScadaTime(), "yyyy-MM-dd HH:mm:ss"), "yyyy-MM")))
                                .collect(Collectors.toList());
                        if (CollUtil.isEmpty(listFinal)) {
                            return;
                        }
                        map.put("dataList", listFinal);
                        break;
                    case 2:
                        listFinal = stationList.stream()
                                .filter(e -> Objects.equals(e.getDeviceId(), deviceId)
                                        && date.equals(DateUtil.format(DateUtil.parse(e.getScadaTime(), "yyyy-MM-dd HH:mm:ss"), "yyyy")))
                                .collect(Collectors.toList());
                        if (CollUtil.isEmpty(listFinal)) {
                            return;
                        }
                        map.put("dataList", listFinal);
                        break;
                }
                res.add(map);
            }
        });
        return res;
    }

    @Override
    public Page<WaterIndexInfoVo> viewScadaDataNew(Page page, CustomDisplayDto dto) {
        if (org.apache.commons.lang3.StringUtils.isBlank(dto.getStartDate())
                || org.apache.commons.lang3.StringUtils.isBlank(dto.getStartDate())) {
            throw new ToflyDeniedException("未传入开始时间和结束时间");
        }
        String simpleDateFormat = "yyyy-MM-dd HH:mm:ss";


        if (org.apache.commons.lang3.StringUtils.isNotBlank(dto.getIsExport()) && dto.getIsExport().equals("1")) {
            List<WaterIndexInfoVo> listInfo = baseMapper.viewScadaDataNew(dto);
            viewScadaDataComm(dto, simpleDateFormat, listInfo);
            ExcelUtils.getListToExcel(WaterIndexInfoVo.class, listInfo, "报表");
        } else {
            Page pageList = baseMapper.viewScadaDataNew(page, dto);
            if (pageList.getTotal() > 0) {
                List<WaterIndexInfoVo> listInfo = pageList.getRecords();
                viewScadaDataComm(dto, simpleDateFormat, listInfo);
            }
            return pageList;
        }
        return page;
    }

    private void viewScadaDataComm(CustomDisplayDto dto, String simpleDateFormat, List<WaterIndexInfoVo> listInfo) {
        listInfo.forEach(item -> {
            String scada_time = null;
            Date startDate = CalendarHelper.ConvertStrToDate(dto.getStartDate(), "yyyy-MM-dd HH:mm:ss");
            switch (dto.getStatisticsType()) {
                case 0:
                    scada_time = CalendarHelper.getFormatDate(startDate, "yyyy-MM-dd");
                    break;
                case 1:
                    scada_time = CalendarHelper.getFormatDate(startDate, "yyyy-MM");
                    break;
                case 2:
                    scada_time = CalendarHelper.getFormatDate(startDate, "yyyy");
                    break;

            }
            ;
            item.setScadaTime(scada_time);
            List<WaterIndexValueVo> indexInfoVoList = this.baseMapper.getWaterIndexInfo(dto.getStatisticsType(), item.getVariableCode(), CalendarHelper.ConvertStrToDate(dto.getStartDate(), simpleDateFormat), CalendarHelper.ConvertStrToDate(dto.getEndDate(), simpleDateFormat));
            item.setDataList(indexInfoVoList);
        });
    }

    /**
     * 手动同步指定日期范围内每天/每月/每年的数据
     *
     * @param codes     指标编码
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @param dateType  类型，1天，2月，3年
     */
    @Override
    public void syncDataRangeComm(String codes, String startDate, String endDate, Integer dateType) {
        List<String> listDates = new ArrayList<>();
        switch (dateType) {
            case 0:
                listDates = CalendarHelper.getHoursBetweenDate(startDate, endDate, "yyyy-MM-dd HH");
                break;
            case 1:
                listDates = CalendarHelper.getDayBetweenDate(startDate, endDate, "yyyy-MM-dd");
                break;
            case 2:
                listDates = CalendarHelper.getMonthBetweenDate(startDate, endDate, "yyyy-MM");
                break;
            case 3:
                listDates = CalendarHelper.getYearBetweenDate(startDate, endDate);
                break;

        }

        listDates.forEach(item -> {
            List<Date> dates = null;
            switch (dateType) {
                case 0:

                    dates = getDateListByType(CalendarHelper.ConvertStrToDate(item, "yyyy-MM-dd HH"), dateType);
                    break;
                case 1:

                    dates = getDateListByType(CalendarHelper.ConvertStrToDate(item, "yyyy-MM-dd"), dateType);
                    break;
                case 2:
                    dates = getDateListByType(CalendarHelper.ConvertStrToDate(item + "-01", "yyyy-MM-dd"), dateType);
                    break;
                case 3:
                    dates = getDateListByType(CalendarHelper.ConvertStrToDate(item + "-01-01", "yyyy-MM-dd"), dateType);
                    break;
            }
            if (!CollectionUtils.isEmpty(dates) && dates.size() == 2) {
                Date start = dates.get(0);
                Date end = dates.get(1);
                syncDataComm(codes, start, end, dateType);
            }

        });

    }

    List<String> getDates(String start, String end, Integer type) {
        List<String> dates = new ArrayList<>();
        Calendar cal = Calendar.getInstance();
        cal.setTime(DateUtil.parse(start, "yyyy-MM-dd HH:mm:ss"));
        while (DateUtil.format(cal.getTime(), "yyyy-MM-dd HH:mm:ss").compareTo(end) <= 0) {
            switch (type) {
                case 0:
                    dates.add(DateUtil.format(cal.getTime(), "yyyy-MM-dd"));
                    cal.add(Calendar.DATE, +1);
                    break;
                case 1:
                    dates.add(DateUtil.format(cal.getTime(), "yyyy-MM"));
                    cal.add(Calendar.MONTH, +1);
                    break;
                case 2:
                    dates.add(DateUtil.format(cal.getTime(), "yyyy"));
                    cal.add(Calendar.YEAR, +1);
                    break;
            }
        }
        return dates;
    }

    private void sendNetCumulativeFlow(Date sDate, Date eDate) {
        List<Scada> scadaList = new ArrayList<>();
        // 获取时间范围内指标类型为正反2,3的监测数据
        List<NetCumulativeFlow> netCumulativeFlow = scadaMapper.getNetCumulativeFlow(DateUtil.format(sDate, "yyyy-MM-dd HH:mm:ss"),
                DateUtil.format(eDate, "yyyy-MM-dd HH:mm:ss"));
        // 根据设备id分组
        Map<Long, List<NetCumulativeFlow>> longListMap = netCumulativeFlow.stream().collect(Collectors.groupingBy(NetCumulativeFlow::getDeviceId));
//        // 处理虚拟指标,获取对应虚拟指标信息
//        Map<Long, Allocation> newAllocation = newAllocation(longListMap.keySet());
        Map<Long, List<Allocation>> allocationListMap = allocationService.list(new QueryWrapper<Allocation>()
                        .lambda().like(Allocation::getVariableCode, "cum_net_flow"))
                .stream().collect(Collectors.groupingBy(Allocation::getDeviceId));
        // 循环处理计算每个设备净累计
        longListMap.forEach((k, v) -> {
            List<Allocation> allocations = allocationListMap.get(k);
            if (CollUtil.isEmpty(allocations)) {
                return;
            }
            Allocation allocation = allocations.get(0);
            // 按照时间分组
            Map<String, List<NetCumulativeFlow>> dateListMap = v.stream().collect(Collectors.groupingBy(NetCumulativeFlow::getScadaTime));
            dateListMap.forEach((n, m) -> {
                AtomicReference<Double> cug = new AtomicReference<>(0.0);
                AtomicReference<Double> neg = new AtomicReference<>(0.0);
                m.forEach(i -> {
                    if (i.getIndexType().equals("2") && i.getValue() != null) {
                        cug.updateAndGet(v1 -> v1 + Double.parseDouble(i.getValue()));
                    }
                    if (i.getIndexType().equals("3") && i.getValue() != null) {
                        neg.updateAndGet(v1 -> v1 + Double.parseDouble(i.getValue()));
                    }
                });
                Scada scada = new Scada();
                scada.setCode(allocation.getVariableCode());
                scada.setScadaTime(DateUtil.parse(n + ":00", "yyyy-MM-dd HH:mm:ss"));
                scada.setUpdateTime(new Date());
                BigDecimal cugDec = BigDecimal.valueOf(cug.get());
                BigDecimal negDec = BigDecimal.valueOf(neg.get());
                scada.setValue(cugDec.subtract(negDec).toString());
//                if (k == 40L || k == 42L || k == 57L || k == 58L || k == 61L) {
//                    scada.setValue(cugDec.toString());
//                }
                scadaList.add(scada);
            });
        });
        logger.info("同步scada新增scada净累计流量历史数据: {}", JSON.toJSONString(scadaList));
        // 新增scada历史数据
        if (scadaList.size() > 0) {
            // 清空时间范围内编码后缀为.JLJ的指标数据
            this.remove(new QueryWrapper<Scada>().lambda().like(Scada::getCode, ".JLJ").ge(Scada::getScadaTime, sDate).le(Scada::getScadaTime, eDate));
            scadaList.forEach(this::save);
            // 获取最大时间数据更新实时数据
            Map<String, List<Scada>> collect = scadaList.stream().collect(Collectors.groupingBy(Scada::getCode));
            List<ScadaMonitor> scadaMonitorList = new ArrayList<>();
            collect.forEach((k, v) -> {
                Optional<Scada> max = v.stream().max(Comparator.comparingDouble(d -> d.getScadaTime().getTime()));
                Scada scada = max.get();
                ScadaMonitor scadaMonitor = new ScadaMonitor(scada.getCode(), scada.getValue(), scada.getScadaTime());
                scadaMonitorList.add(scadaMonitor);
            });
            List<ScadaMonitor> scadaMonitors = scadaMonitorService.list(new QueryWrapper<ScadaMonitor>().lambda().like(ScadaMonitor::getCode, ".JLJ"));
            List<String> scadaMonitorCodes = new ArrayList<>();
            if (CollUtil.isNotEmpty(scadaMonitors)) {
                scadaMonitorCodes = scadaMonitors.stream().map(ScadaMonitor::getCode).collect(Collectors.toList());
            }
            List<String> finalScadaMonitorCodes = scadaMonitorCodes;
            scadaMonitorList.forEach(info -> {
                if (finalScadaMonitorCodes.contains(info.getCode())) {
                    scadaMonitorService.update(info, new QueryWrapper<ScadaMonitor>().lambda().eq(ScadaMonitor::getCode, info.getCode()));
                } else {
                    scadaMonitorService.save(info);
                }
            });
        }
    }

    /**
     * 处理虚拟指标
     *
     * @param deviceIdList
     * @return
     */
    private Map<Long, Allocation> newAllocation(Set<Long> deviceIdList) {
        // 存放需要新增的虚拟指标列表
        Set<Long> xnDeviceIdSet = new HashSet<>();
        // 获取虚拟指标
        List<Allocation> allocations = allocationMapper.selectList(new QueryWrapper<Allocation>().lambda().eq(Allocation::getType, "19"));
        if (null != allocations && allocations.size() > 0) {
            // 虚拟指标设备id分组 一个设备只能有一个虚拟指标
            Map<Long, List<Allocation>> xnCodeMap = allocations.stream().collect(Collectors.groupingBy(Allocation::getDeviceId));
            Set<Long> collect = deviceIdList.stream().filter(deviceId -> !xnCodeMap.containsKey(deviceId)).collect(Collectors.toSet());
            xnDeviceIdSet.addAll(collect);
        } else {
            // 全部赋值需要新增虚拟表的设备
            xnDeviceIdSet.addAll(deviceIdList);
        }
        // 新增虚拟指标
        List<Allocation> newAllocationList = new ArrayList<>();
        if (xnDeviceIdSet.size() > 0) {

            // 获取需要新增虚拟的指标设备列表
            List<Allocation> oldAllocationList = allocationMapper.selectList(new QueryWrapper<Allocation>().lambda().in(Allocation::getDeviceId, xnDeviceIdSet));
            // 按照设备id分组
            Map<Long, List<Allocation>> mapAllocation = oldAllocationList.stream().collect(Collectors.groupingBy(Allocation::getDeviceId));
            mapAllocation.forEach((k, v) -> {
                // 判断当前设备下是否存在正向指标 若不存在则获取反向属性
                Map<String, Allocation> allocationMap = v.stream().collect(Collectors.toMap(Allocation::getType, t -> t, (key1, key2) -> key2));
                Allocation allocation;
                if (allocationMap.containsKey("2")) {
                    allocation = allocationMap.get("2");
                } else {
                    allocation = allocationMap.get("3");
                }
                allocation.setId(null);
                allocation.setVariableCode(allocation.getVariableCode() + ".JLJ");
                allocation.setVariableName(allocation.getVariableName() + ".净累计");
                allocation.setDisplayName(allocation.getDisplayName() + ".净累计");

                allocation.setType("4");
                newAllocationList.add(allocation);
            });

            // 新增指标记录
            // 同步到区域表
            if (newAllocationList.size() > 0) {
                allocationService.saveBatch(newAllocationList);
                List<Dmatable> dmaTableList = newAllocationList.stream().map(v -> new Dmatable(v.getDeviceId(), Double.parseDouble("0"))).collect(Collectors.toList());
                dmatableService.saveBatch(dmaTableList);
            }
        }

        // 返回所有 设备id 虚拟指标设备
        if (null != allocations && allocations.size() > 0) {
            newAllocationList.addAll(allocations);
        }
        return newAllocationList.stream().collect(Collectors.toMap(Allocation::getDeviceId, t -> t, (key1, key2) -> key2));
    }

    /**
     * 查询设备运行时长
     *
     * @param codes
     * @param startTime
     * @param endTime
     * @param dateType
     * @param scadaLists
     */
    private void calRunTime(List<String> codes, Date startTime, Date endTime, Integer dateType, List<StatisticsScadaCalVo> scadaLists) {
        /*List<StatisticsScadaCalVo> vos = scadaMapper.selectHistoryRunTimeByScadaTime(codes, startTime, endTime, dateType);
        if (CollectionUtils.isEmpty(vos) || CollectionUtils.isEmpty(scadaLists)) {
            return;
        }
        for (StatisticsScadaCalVo scadaList : scadaLists) {
            if (!StringUtils.hasText(scadaList.getRunTime())) {
                scadaList.setRunTime("0");
            }
            BigDecimal sum = new BigDecimal(scadaList.getRunTime());
            BigDecimal sumFail = new BigDecimal(scadaList.getRunTime());
            for (StatisticsScadaCalVo vo : vos) {
                if (scadaList.getCode().equals(vo.getCode()) && vo.getRunTime() != null
                && vo.getValue().equals("1")) {
                    BigDecimal decimal2 = new BigDecimal(vo.getRunTime());
                    sum = sum.add(decimal2);
                }
                if (scadaList.getCode().equals(vo.getCode()) && vo.getRunTime() != null
                        && vo.getValue().equals("0")) {
                    BigDecimal decimal2 = new BigDecimal(vo.getRunTime());
                    sumFail = sumFail.add(decimal2);
                }
            }
            String s = sum.stripTrailingZeros().toPlainString();
            if (s.contains(".")){
                s = s.substring(0, s.lastIndexOf("."));
            }
            scadaList.setRunTime(s);

            String err = sum.stripTrailingZeros().toPlainString();
            if (err.contains(".")){
                err = err.substring(0, err.lastIndexOf("."));
            }
            scadaList.setRunTime(err);
        }*/
    }


    /**
     * 特殊值处理
     *
     * @param statisticsScadaVoPage 数据集合
     */
    private void defaultValHandle(Page<StatisticsScadaVo> statisticsScadaVoPage) {
        for (StatisticsScadaVo scadaVo : statisticsScadaVoPage.getRecords()) {
            if (!StringUtils.hasText(scadaVo.getValue())) {
                scadaVo.setValue("0");
            } else if (StringUtils.hasText(scadaVo.getValue()) && scadaVo.getValue().startsWith(".")) {
                String value = scadaVo.getValue();
                scadaVo.setValue("0" + value);
            }
        }
    }

    /**
     * 截取数组长度批量插入
     *
     * @param scadas    数据集合
     * @param tableName 表名
     * @return 成功条数
     */
    public int insertMouthDataBatch(List<Scada> scadas, String tableName) {
        int size = scadas.size();
        int limit = 1000;
        int startIndex = 0;
        int endIndex;
        if (size <= limit) {
            //添加新数据
            int insertMouth = scadaMapper.insertMouth(scadas, tableName);
            log.info("新增数据：{}条", insertMouth);
        } else {
            while (startIndex < size) {
                //结束索引范围
                endIndex = startIndex + limit;
                //最后索引不能超过本身长度
                endIndex = endIndex > size ? size : endIndex;
                //截取数据
                List<Scada> scadaList = scadas.subList(startIndex, endIndex);
                //下次索引位置
                startIndex = endIndex;
                //添加新数据
                int insertMouth = scadaMapper.insertMouth(scadaList, tableName);
                log.info("{}新增数据：{}条", tableName, insertMouth);
            }
        }
        return size;
    }


    public void insertMouth(List<Scada> scadas, String tableName) {
        int size = scadas.size();
        int i = 0;
        while (size > 600) {
            scadaMapper.insertMouth(scadas.subList(i, i + 600), tableName);
            i = i + 600;
            size = size - 600;
        }
        if (size > 0) {
            scadaMapper.insertMouth(scadas.subList(i, i + size), tableName);
        }
    }


    /**
     * 截取数组长度批量插入
     *
     * @param scadas    数据集合
     * @param tableName 表名
     * @return 成功条数
     */
    public int insertMouthBatch(List<Scada> scadas, String tableName) {
        int size = scadas.size();
        int limit = 1000;
        int startIndex = 0;
        int endIndex;
        if (size <= limit) {
            //添加新数据
            int insertMouth = scadaMapper.insertMouth(scadas, tableName);
            log.info("新增数据：{}条", insertMouth);
        } else {
            while (startIndex < size) {
                //结束索引范围
                endIndex = startIndex + limit;
                //最后索引不能超过本身长度
                endIndex = endIndex > size ? size : endIndex;
                //截取数据
                List<Scada> scadaList = scadas.subList(startIndex, endIndex);
                //下次索引位置
                startIndex = endIndex;
                //添加新数据
                int insertMouth = scadaMapper.insertMouth(scadaList, tableName);
                log.info("新增数据：{}条", insertMouth);
            }
        }
        return size;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void origDeviceAndIndex() {
        List<DeviceArchiveManage> manages = deviceArchiveManageService.list();
        // 存储设备和指标数据
        List<Allocation> parameterList = new ArrayList<>();
        List<Allocation> parameters = allocationService.list();
        for (DeviceArchiveManage deviceArchiveManage : manages) {
            List<Allocation> parametersFinal = new ArrayList<>();
            if (CollUtil.isNotEmpty(parameters)) {
                parametersFinal = parameters.stream()
                        .filter(e -> Objects.equals(e.getDeviceId(), deviceArchiveManage.getId()))
                        .collect(Collectors.toList());
            }
            parameterList.addAll(saveParameters(deviceArchiveManage, parametersFinal));
        }
        if (CollUtil.isNotEmpty(parameterList)) {
            parameterList = parameterList.stream().collect(Collectors.collectingAndThen(Collectors
                    .toCollection(() -> new TreeSet<>(Comparator.comparing(Allocation::getVariableCode))), ArrayList::new));
            parameterList.forEach(allocationService::save);
        }
        List<String> monitorCodeList = scadaMonitorService.list().stream().map(ScadaMonitor::getCode).collect(Collectors.toList());
        List<Allocation> parameterAll = allocationService.list();
        List<Allocation> parameterPart = parameterAll.stream().filter(e -> !monitorCodeList.contains(e.getVariableCode())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(parameterPart)) {
            if (parameterPart.size() <= 500) {
                allocationService.remove(new QueryWrapper<Allocation>().lambda().in(Allocation::getVariableCode, parameterPart.stream().map(Allocation::getVariableCode).collect(Collectors.toList())));
            } else {
                int times = parameterPart.size() / 500;
                for (int i = 0; i <= times; i++) {
                    List<Allocation> allocationListFinal = parameterPart.subList(i * 500, Math.min((i + 1) * 500, parameterPart.size()));
                    if (CollUtil.isEmpty(allocationListFinal)) {
                        continue;
                    }
                    allocationService.remove(new QueryWrapper<Allocation>().lambda().in(Allocation::getVariableCode, allocationListFinal.stream().map(Allocation::getVariableCode).collect(Collectors.toList())));
                }
            }
        }
    }

    @Override
    public void synStatisticsScada(Integer type, String startTime, String endTime) {
        if (type == 0) {
            scadaHandler.syncTwentyFourHourData(DateUtil.parse(startTime, "yyyy-MM-dd HH:mm:ss"), DateUtil.parse(endTime, "yyyy-MM-dd HH:mm:ss"));
        } else if (type == 1) {
            scadaHandler.saveHistoryCode(DateUtil.parse(startTime, "yyyy-MM-dd HH:mm:ss"), DateUtil.parse(endTime, "yyyy-MM-dd HH:mm:ss"), type);
        } else {
            scadaHandler.saveStatisticsCode(DateUtil.parse(startTime, "yyyy-MM-dd HH:mm:ss"), DateUtil.parse(endTime, "yyyy-MM-dd HH:mm:ss"), type);
        }
    }

    @Override
    public void synScadaDiffFlow(String startTime, String endTime) {
        scadaHandler.sendCumulativeFlowDifference(startTime, endTime);
    }

    @Override
    public void synHzFlowMeterHis(String startTime, String endTime) {
        Map<Object, Object> map = MapUtil.newHashMap(8);
        map.put("startDate", startTime);
        map.put("endDate", endTime);
        map.put("typeId", 2);
        map.put("pageNumber", 1);
        map.put("pageSize", 5000);
        Map<String, Object> paramMap = new HashMap<>(12);
        byte[] keyBytes = privateKey.getBytes();
        AES aes = SecureUtil.aes(keyBytes);
        paramMap.put("data", aes.encryptHex(JSONUtil.toJsonStr(map)));

        String urlSuffix = "wisdom/openService/api/listMeterHistory";
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000L);
        String signature = SecureUtil.md5(appId + timeStamp + privateKey).toUpperCase();
        String body = HttpRequest.post(urlPrefix + urlSuffix)
                .header("Content-Type", "multipart/form-data; boundary=<calculated when request is sent>")
                .header("AppId", appId)
                .header("Timestamp", timeStamp)
                .header("Signature", signature)
                .form(paramMap).execute().body();
        String data = JSONUtil.parseObj(body).getStr("data");
        String dataInfo = aes.decryptStr(data);
        List<HzFlowMeterHisVo> dataInfoList = JSON.parseArray(dataInfo, HzFlowMeterHisVo.class);
        if (CollUtil.isEmpty(dataInfoList)) {
            return;
        }
        List<Scada> scadaHistoryList = new ArrayList<>();
        List<String> deviceCodeList = deviceArchiveManageService.list().stream().map(DeviceArchiveManage::getCode).collect(Collectors.toList());
        dataInfoList.forEach(info -> {
            if (!deviceCodeList.contains(String.valueOf(info.getMeterCode()))) {
                return;
            }

            if (info.getInstantFlow() != null) {
                Scada instantFlow = new Scada();
                instantFlow.setScadaTime(DateUtil.parse(info.getCollectTime(), "yyyy-MM-dd HH:mm:ss"));
                instantFlow.setCode(info.getMeterCode() + "_instantaneous_flow");
                instantFlow.setValue(String.valueOf(info.getInstantFlow()));
                instantFlow.setUpdateTime(DateUtil.parse(info.getReportTime(), "yyyy-MM-dd HH:mm:ss"));
                scadaHistoryList.add(instantFlow);
            }

            if (info.getAccumulateFlow() != null) {
                Scada accumulateFlow = new Scada();
                accumulateFlow.setScadaTime(DateUtil.parse(info.getCollectTime(), "yyyy-MM-dd HH:mm:ss"));
                accumulateFlow.setCode(info.getMeterCode() + "_cum_flow");
                accumulateFlow.setValue(String.valueOf(info.getAccumulateFlow()));
                accumulateFlow.setUpdateTime(DateUtil.parse(info.getReportTime(), "yyyy-MM-dd HH:mm:ss"));
                scadaHistoryList.add(accumulateFlow);
            }

            if (info.getTemperature() != null) {
                Scada temperature = new Scada();
                temperature.setScadaTime(DateUtil.parse(info.getCollectTime(), "yyyy-MM-dd HH:mm:ss"));
                temperature.setCode(info.getMeterCode() + "_temperature");
                temperature.setValue(String.valueOf(info.getTemperature()));
                temperature.setUpdateTime(DateUtil.parse(info.getReportTime(), "yyyy-MM-dd HH:mm:ss"));
                scadaHistoryList.add(temperature);
            }

            if (info.getPressure() != null) {
                Scada pressure = new Scada();
                pressure.setScadaTime(DateUtil.parse(info.getCollectTime(), "yyyy-MM-dd HH:mm:ss"));
                pressure.setCode(info.getMeterCode() + "_pressure");
                pressure.setValue(String.valueOf(info.getPressure()));
                pressure.setUpdateTime(DateUtil.parse(info.getReportTime(), "yyyy-MM-dd HH:mm:ss"));
                scadaHistoryList.add(pressure);
            }
        });
        if (CollUtil.isEmpty(scadaHistoryList)) {
            return;
        }
        List<String> scadaHistoryCodeList = scadaHistoryList.stream().map(Scada::getCode).distinct().collect(Collectors.toList());
        this.remove(new QueryWrapper<Scada>()
                .between("to_char(UPDATE_TIME, 'yyyy-mm-dd hh24:mi:ss')", startTime, endTime)
                .in("CODE", scadaHistoryCodeList));
        List<Scada> scadaHistoryNewList = scadaHistoryList.stream()
                .sorted(Comparator.comparing(Scada::getUpdateTime).reversed())
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(o -> o.getCode() + "-"
                                + DateUtil.format(o.getScadaTime(), "yyyyMMddHHmm")))), ArrayList::new));
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                4,
                4,
                120,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(50),
                (r) -> {
                    Thread t = new Thread(r);
                    t.setName("thread-" + ((int) (Math.random() * 1000)));
                    return t;
                },
                new ThreadPoolExecutor.CallerRunsPolicy());
        //为了防止SQL语句超出长度出错，分成几次插入
        if (scadaHistoryNewList.size() <= 200) {
            threadPoolExecutor.execute(() -> this.saveBatch(scadaHistoryNewList));
        } else {
            int times = scadaHistoryNewList.size() / 200;
            for (int i = 0; i <= times; i++) {
                List<Scada> scadaHisListFinal = scadaHistoryNewList.subList(i * 200, Math.min((i + 1) * 200, scadaHistoryNewList.size()));
                if (CollUtil.isNotEmpty(scadaHisListFinal)) {
                    threadPoolExecutor.execute(() -> this.saveBatch(scadaHisListFinal));
                }
            }
        }

        // 更新实时数据
        List<Scada> list = scadaHistoryNewList;
        list = list.stream()
                .sorted(Comparator.comparing(Scada::getScadaTime).reversed())
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Scada::getCode))), ArrayList::new));
        List<ScadaMonitor> indexValNewList = new ArrayList<>();
        List<ScadaMonitor> indexValOldList = new ArrayList<>();
        // 查询实时表所有指标
        List<String> monitorCodeList = scadaMonitorService.list().stream().map(ScadaMonitor::getCode).collect(Collectors.toList());
        for (Scada scada : list) {
            ScadaMonitor indexVal = new ScadaMonitor();
            indexVal.setCode(scada.getCode());
            indexVal.setValue(scada.getValue());
            indexVal.setScadaTime(scada.getScadaTime());
            indexVal.setUpdateTime(scada.getUpdateTime());
            if (monitorCodeList.contains(scada.getCode())) {
                indexValOldList.add(indexVal);
            } else {
                indexVal.setId(SnowFlake.nextId());
                indexValNewList.add(indexVal);
            }
        }
        if (CollUtil.isNotEmpty(indexValNewList)) {
            scadaMonitorService.saveBatch(indexValNewList);
        }
        if (CollUtil.isNotEmpty(indexValOldList)) {
            indexValOldList.forEach(info -> scadaMonitorService.update(info, new QueryWrapper<ScadaMonitor>().lambda().eq(ScadaMonitor::getCode, info.getCode())));
        }
    }

    @Override
    public void synHzFlowMeterInfo(String startTime, String endTime) {
        Map<Object, Object> map = MapUtil.newHashMap(8);
        map.put("startDate", startTime);
        map.put("endDate", endTime);
        map.put("typeId", 3);
        map.put("pageNumber", 1);
        map.put("pageSize", 2000);
        Map<String, Object> paramMap = new HashMap<>(12);
        byte[] keyBytes = privateKey.getBytes();
        AES aes = SecureUtil.aes(keyBytes);
        paramMap.put("data", aes.encryptHex(JSONUtil.toJsonStr(map)));

        String urlSuffix = "wisdom/openService/api/listMeterInfo";
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000L);
        String signature = SecureUtil.md5(appId + timeStamp + privateKey).toUpperCase();
        String body = HttpRequest.post(urlPrefix + urlSuffix)
                .header("Content-Type", "multipart/form-data; boundary=<calculated when request is sent>")
                .header("AppId", appId)
                .header("Timestamp", timeStamp)
                .header("Signature", signature)
                .form(paramMap).execute().body();
        String data = JSONUtil.parseObj(body).getStr("data");
        String dataInfo = aes.decryptStr(data);
        List<HzFlowMeterInfoVo> dataInfoList = JSON.parseArray(dataInfo, HzFlowMeterInfoVo.class);
        List<String> deviceList = deviceArchiveManageService.list().stream().map(DeviceArchiveManage::getCode).collect(Collectors.toList());
        List<DeviceArchiveManage> deviceNewList = new ArrayList<>();
        dataInfoList.forEach(info -> {
            if (deviceList.contains(info.getFactoryNumber())) {
                return;
            }
            DeviceArchiveManage device = new DeviceArchiveManage();
            device.setCode(info.getFactoryNumber());
            device.setName(info.getMeterName());
            device.setType("2");
            device.setDeviceType("3");
            device.setModelSpecification(info.getDiameter());
            device.setAddress(info.getAddress());
            device.setLatitude(org.apache.commons.lang3.StringUtils.isBlank(info.getLatitude()) ? null:new BigDecimal(info.getLatitude().trim()));
            device.setLongitude(org.apache.commons.lang3.StringUtils.isBlank(info.getLongitude()) ? null:new BigDecimal(info.getLongitude().trim()));
            deviceNewList.add(device);
        });
        if (CollUtil.isNotEmpty(deviceNewList)) {
            deviceArchiveManageService.saveBatch(deviceNewList);
        }
    }

    @Override
    public void autoIndexAlarmStatus() {
        List<ScadaMonitor> monitorIndexList = scadaMonitorService.list();
        List<Allocation> allocationList = allocationService.list();
        List<ScadaReport> scadaReportList = scadaReportService.list();
        allocationList.forEach(allocation -> {
            allocation.setIsAlarm("0");

            List<ScadaReport> scadaReportListFinal = scadaReportList.stream()
                    .filter(e -> e.getAllocationId().equals(allocation.getType())).collect(Collectors.toList());
            if (CollUtil.isEmpty(scadaReportListFinal)) {
                return;
            }
            List<String> reportIdList = scadaReportListFinal.stream().map(f -> String.valueOf(f.getId())).collect(Collectors.toList());
            String reportIds = String.join(",", reportIdList);
            allocation.setReportId(reportIds);

            List<ScadaMonitor> monitorIndexListFinal = monitorIndexList.stream()
                    .filter(e -> e.getCode().equals(allocation.getVariableCode())).collect(Collectors.toList());
            if (CollUtil.isEmpty(monitorIndexListFinal)) {
                return;
            }
            BigDecimal indexValue = new BigDecimal(monitorIndexListFinal.get(0).getValue());

            scadaReportListFinal.forEach(scadaReport -> {
                if (indexValue.compareTo(new BigDecimal(scadaReport.getReportLower())) >= 0
                        && indexValue.compareTo(new BigDecimal(scadaReport.getReportUpper())) <= 0) {
                    allocation.setIsAlarm("1");
                }
            });
        });
        allocationService.updateBatchById(allocationList);
    }


    /* ***************************************************自定义方法******************************************** */


    private List<Allocation> saveParameters(DeviceArchiveManage deviceArchiveManage, List<Allocation> parameters) {
        List<Allocation> allocations = new ArrayList<>();

        List<IndexTypeInfoEnum.IndexTypeInfoVo> indexTypeInfoVoList = IndexTypeInfoEnum.getIndexTypeInfoList();
        indexTypeInfoVoList.forEach(indexTypeInfoVo -> {
            List<Allocation> list = parameters.stream()
                    .filter(e -> e.getVariableCode().equals(deviceArchiveManage.getCode() + "_" + indexTypeInfoVo.getCode()))
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(list)) {
                return;
            }
            Allocation allocation = new Allocation();
            allocation.setDisplayName(indexTypeInfoVo.getName());
            allocation.setVariableCode(deviceArchiveManage.getCode() + "_" + indexTypeInfoVo.getCode());
            allocation.setVariableName(deviceArchiveManage.getCode() + "_" + indexTypeInfoVo.getName());
            allocation.setDeviceId(deviceArchiveManage.getId());
            allocation.setUnit(indexTypeInfoVo.getUnit());
            allocation.setType(indexTypeInfoVo.getId());
            allocation.setAccuracy(indexTypeInfoVo.getAccuracy());
            allocations.add(allocation);
        });

        return allocations;
    }
}
