package com.tofly.scada.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
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.tofly.common.core.util.StringUtil;
import com.tofly.common.oauth.exception.ToflyDeniedException;
import com.tofly.scada.common.FlowConstant;
import com.tofly.scada.common.MessageTopicEnum;
import com.tofly.scada.entity.*;
import com.tofly.scada.entity.dto.DeviceQuery;
import com.tofly.scada.entity.dto.ScadaQuery;
import com.tofly.scada.entity.vo.*;
import com.tofly.scada.mapper.*;
import com.tofly.scada.service.*;

import com.tofly.scada.util.SendTidingsUtil;
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.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
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.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
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 {

    /**
     * 统计量放行记录的code
     */
//    @Value("${statisticsScadaNew.code}")
    private List<String> llj = new ArrayList<>();
    private final Logger logger = LoggerFactory.getLogger(ScadaServiceImpl.class);
    private static final Pattern pattern = Pattern.compile("-?[0-9]+.?[0-9]*");


    public static final String SCADA_HISTORY = "SCADA_HISTORY";
    @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;

    /**
     * 根据当前时间动态自动创建月份表，创建完成之后返回表名
     * <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小时数据，支持自定义时间添加
     */
    //每小时执行一次（整点整分）
    @Scheduled(cron = "0 0 * * *  ? ")
    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小时表：正在进行查询历史数据");
        //查询历史数据
//        List<Scada> scadas = getScadasDatas(code, startTime, endTime);
        List<String> codes = getCodes(null);
        // 获取对应时间范围内数据参数
        List<StatisticsScadaCalVo> scadaLists = scadaMapper.selectHistoryMaxAndMinByScadaTime(codes, startTime, endTime, 0);
        // 删除旧数据
        statisticsScadaService.delete(0, codes, startTime, endTime);
        //保存数据
        saveStatisticsCode(scadaLists, FlowConstant.INTEGER_ZERO, true);
        logger.info("同步scada历史数据到24小时表结束");
    }

    public void insert24H(String code, Date startTime, Date endTime, Integer type) {
        logger.info("同步scada历史数据到24小时表：正在进行时间调整");
        List<String> codes = getCodes(code);
        // 获取对应时间范围内数据参数
        List<StatisticsScadaCalVo> scadaLists = scadaMapper.selectHistoryMaxAndMinByScadaTime(codes, startTime, endTime, type);
        // 删除旧数据
        statisticsScadaService.delete(0, codes, startTime, endTime);
        //保存数据
        saveStatisticsCode(scadaLists, FlowConstant.INTEGER_ZERO, false);
    }

    private Date getDateType(String timeStr, Integer type) {
        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 {
            String s = timeStr + "-01";
            return TimeUtils.localDateToDate(TimeUtils.fromString2LocalDate(s, "yyyy-MM-dd"));
        }
    }

    /**
     * 同步每天数据，每天凌晨一点半同步昨天的数据
     */
    @Scheduled(cron = "0 30 1 * * ? ")
    public void syncDaysData() {
        logger.info("同步scada历史数据到表：正在进行时间调整");
        //整个日历调整时间
        Calendar calendar = Calendar.getInstance();
        Date date = new Date();
        calendar.setTime(date);
        //延迟一天
        calendar.add(Calendar.DAY_OF_MONTH, -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");
        //拉长时间到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");
        logger.info("同步scada历史数据到24小时表：正在进行查询历史数据");
        //查询历史数据
//        List<Scada> scadas = getScadasDatas(code, startTime, endTime);
        List<String> codes = getCodes(null);
        // 获取对应时间范围内数据参数
        List<StatisticsScadaCalVo> scadaLists = scadaMapper.selectHistoryMaxAndMinByScadaTime(codes, startTime, endTime, 1);
        // 删除旧数据
        statisticsScadaService.delete(1, codes, startTime, endTime);
        saveStatisticsCode(scadaLists, FlowConstant.INTEGER_ONE, true);

    }


    @Async
    public void syncDaysData(String code, Date startTime, Date endTime, Integer type) {
        logger.info("同步scada历史数据到表：正在进行时间调整");
        List<String> codes = getCodes(code);
        // 获取对应时间范围内数据参数
        List<StatisticsScadaCalVo> scadaLists = scadaMapper.selectHistoryMaxAndMinByScadaTime(codes, startTime, endTime, type);
        // 删除旧数据
        statisticsScadaService.delete(1, codes, startTime, endTime);
        saveStatisticsCode(scadaLists, FlowConstant.INTEGER_ONE, true);

    }


    /**
     * 每月1号凌晨1点同步更新历史数据与上个月表数据，支持自定义时间添加
     */
    @Scheduled(cron = "0 0 1 1 * ? ")
    public void syncDataIntoMonths() {
        logger.info("同步scada历史数据到月份表：正在进行时间调整");
        //整个日历调整时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        //倒退一个月开始时间
        calendar.add(Calendar.MONTH, -1);
        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();
        String end = DateUtil.format(calendar.getTime(), "yyyy-MM-dd HH:mm:ss");
        logger.info("同步scada历史数据到月份表：正在进行历史数据查询....");
        //查询历史数据
//        List<Scada> scadas = getScadasDatas(code, startTime, endTime);
        List<String> codes = getCodes(null);
        // 获取对应时间范围内数据参数
        List<StatisticsScadaCalVo> scadaLists = scadaMapper.selectHistoryMaxAndMinByScadaTime(codes, startTime, endTime, 2);
        // 删除旧数据
        statisticsScadaService.delete(2, codes, startTime, endTime);
        saveStatisticsCode(scadaLists, FlowConstant.INTEGER_TWO, true);
    }

    @Async
    public void syncDataIntoMonths(String code, Date startTime, Date endTime, Integer type) {
        logger.info("同步scada历史数据到月份表：正在进行时间调整");
        List<String> codes = getCodes(code);
        // 获取对应时间范围内数据参数
        List<StatisticsScadaCalVo> scadaLists = scadaMapper.selectHistoryMaxAndMinByScadaTime(codes, startTime, endTime, 2);
        // 删除旧数据
        statisticsScadaService.delete(2, codes, startTime, endTime);
        saveStatisticsCode(scadaLists, FlowConstant.INTEGER_TWO, true);
    }


    /**
     * 普通进程跑
     *
     * @param startTime
     * @param endTime
     * @param codes
     * @param DateType
     */
    public void saveStatisticsByCode(Date startTime, Date endTime, List<String> codes, Integer DateType) {
//        if (!CollectionUtils.isEmpty(codes)) {
//            logger.info("同步scada历史数据到表：正在进行数据分组");
//            //有数据就分组
//            logger.info("同步scada历史数据到表：正在进行分组循环计算");
//            //分组循环计算添加
//            for (String code : codes) {
//                //当前线程名称
//                String name = Thread.currentThread().getName();
//                logger.info("当前线程：{}----统计添加数据：{}中。。。。", name, code);
//                QueryWrapper<Scada> scadaQueryWrapper = new QueryWrapper<>();
//                scadaQueryWrapper.between("SCADA_TIME", startTime, endTime)
//                        .in("CODE", code);
//                List<Scada> scadaList = scadaMapper.selectList(scadaQueryWrapper);
//                if (CollectionUtils.isEmpty(scadaList)) {
//                    return;
//                }
//
//                String defaultValue = "0";
//
//                BigDecimal sum = BigDecimal.ZERO;
//                BigDecimal average = BigDecimal.ZERO;
//                BigDecimal maxVal = new BigDecimal(scadaList.get(0).getValue() == null ? "0" : scadaList.get(0).getValue());
//                BigDecimal minVal = new BigDecimal(scadaList.get(0).getValue() == null ? "0" : scadaList.get(0).getValue());
//
//                for (String s : llj) {
//                    if (scadaList.get(0).getCode().contains(s)) {
//                        Iterator<Scada> iterator = scadaList.iterator();
//                        while (iterator.hasNext()) {
//                            Scada next = iterator.next();
//                            for (String s1 : llj) {
//                                if (next.getCode().contains(s1) && !next.getValue().equals(defaultValue)) {
//                                    maxVal = new BigDecimal(next.getValue());
//                                    minVal = maxVal;
//                                    break;
//                                }
//                            }
//                        }
//                    }
//                }
//
//
//                for (Scada scada : scadaList) {
//                    for (String s : llj) {
//                        if (scada.getCode().contains(s) && scada.getValue().equals(defaultValue)) {
//                            continue;
//                        }
//                    }
//                    BigDecimal decimal = new BigDecimal(scada.getValue());
//                    sum = decimal.add(sum);
//                    if (decimal.compareTo(maxVal) > 0) {
//                        maxVal = decimal;
//                    }
//                    if (decimal.compareTo(minVal) < 0) {
//                        minVal = decimal;
//                    }
//                }
//                average = sum.divide(BigDecimal.valueOf(scadaList.size()), RoundingMode.HALF_UP);
//
//                StatisticsScada statisticsScada = new StatisticsScada();
//                statisticsScada.setCode(scadaList.get(0).getCode());
//                statisticsScada.setValue(scadaList.get(scadaList.size() - 1).getValue());
//                statisticsScada.setAve(average.toString());
//                statisticsScada.setMax(maxVal.toString());
//                statisticsScada.setMin(minVal.toString());
//                statisticsScada.setFold(sum.toString());
//                statisticsScada.setStartTime(startTime);
//                statisticsScada.setEndTime(endTime);
//                statisticsScada.setStatisticsType(DateType);
//
//                //查询指标报警上下限计算合格率相关
//                QueryWrapper<Allocation> allocationQueryWrapper = new QueryWrapper<>();
//                allocationQueryWrapper.eq("VARIABLE_CODE", scadaList.get(0).getCode());
//                List<Allocation> allocations = allocationMapper.selectList(allocationQueryWrapper);
//                if (!CollectionUtils.isEmpty(allocations) && StringUtils.hasText(allocations.get(0).getType())) {
//                    Set<Long> reportIdSets = getReportIdSets(allocations);
//                    if (!CollectionUtils.isEmpty(reportIdSets)) {
//                        logger.info("计算指标报警上下限计算合格率相关....");
//                        QueryWrapper<ScadaReport> scadaReportQueryWrapper = new QueryWrapper<>();
//                        scadaReportQueryWrapper.in("ID", reportIdSets);
//                        List<ScadaReport> scadaReports = reportMapper.selectList(scadaReportQueryWrapper);
//                        if (!CollectionUtils.isEmpty(scadaReports)) {
//                            //超标次数
//                            int errorCount = 0;
//                            //不合格超标时长
//                            long errorTimeTotal = 0;
//                            //超标总时长
//                            long timeTotal = (scadaList.get(scadaList.size() - 1).getScadaTime().getTime()
//                                    - scadaList.get(0).getScadaTime().getTime()) / 1000;
//                            //错误时间
//                            Date errorTime = null;
//                            for (Scada scada : scadaList) {
//                                for (String s : llj) {
//                                    if (scada.getCode().contains(s) && scada.getCode().equals("0")) {
//                                        continue;
//                                    }
//                                }
//                                for (ScadaReport report : scadaReports) {
//                                    if (Double.parseDouble(scada.getValue()) < Double.parseDouble(report.getReportLower())
//                                            || Double.parseDouble(scada.getValue()) > Double.parseDouble(report.getReportUpper())) {
//                                        errorCount = errorCount + 1;
//                                        errorTime = scada.getScadaTime();
//                                        break;
//                                    } else if (errorTime != null) {
//                                        errorTimeTotal = errorTimeTotal +
//                                                (scada.getScadaTime().getTime() - errorTime.getTime()) / 1000;
//                                    }
//                                }
//                            }
//                            //合格率
//                            float pass = 0;
//                            if (timeTotal != 0) {
//                                BigDecimal num1 = new BigDecimal(String.valueOf(errorTimeTotal));
//                                BigDecimal num2 = new BigDecimal(String.valueOf(timeTotal));
//                                pass = num1.divide(num2, RoundingMode.HALF_UP).floatValue();
//                            }
//
//                            statisticsScada.setErrorCount(String.valueOf(errorCount));
//                            statisticsScada.setErrorTimeTotal(String.valueOf(errorTimeTotal == 0 ? timeTotal : errorTimeTotal));
//                            statisticsScada.setPass(String.valueOf(pass));
//                            statisticsScada.setTimeTotal(String.valueOf(timeTotal));
//                        }
//                    }
//                    statisticsScada.setDataType(String.valueOf(allocations.get(0).getType()));
//                }
//
//                //删除旧数据
//
//                int delete = statisticsScadaService.delete(DateType, statisticsScada.getCode(),
//                        DateUtil.format(statisticsScada.getStartTime(), "yyyy-MM-dd HH:mm:ss"),
//                        DateUtil.format(statisticsScada.getEndTime(), "yyyy-MM-dd HH:mm:ss"));
//                logger.info("成功删除{}条旧数据", delete);
//                //添加数据
//                logger.info("统计添加数据.....：{}", statisticsScada);
//                try {
//                    statisticsScadaService.save(statisticsScada);
//                    logger.info("统计添加数据完成：{}", statisticsScada);
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    log.error("统计数据添加出现错误----数据：{}---错误:{}", statisticsScada, e.getMessage());
//                }
//                logger.info("统计添加数据完成：{}", statisticsScada);
//            }
//        } else {
//            logger.info("获取历史数据为空");
//        }
    }


    /**
     * 设置统计数据
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param scadas    数据集合
     */
    @Async
    public void saveStatisticsData(Date startTime, Date endTime, List<Scada> scadas, Integer DateType) {
//        if (!CollectionUtils.isEmpty(scadas)) {
//            logger.info("同步scada历史数据到表：正在进行数据分组");
//            //有数据就分组
//            Map<String, List<Scada>> listMap = scadas.stream().filter(scada -> scada.getCode() != null && scada.getValue() != null)
//                    .collect(Collectors.groupingBy(Scada::getCode));
//            logger.info("同步scada历史数据到表：正在进行分组循环计算");
//            ExecutorService executorService = Executors.newFixedThreadPool(listMap.entrySet().size());
//            CountDownLatch countDownLatch = new CountDownLatch(listMap.entrySet().size());
//            //分组循环计算添加
//            for (Map.Entry<String, List<Scada>> entity : listMap.entrySet()) {
//                //开启多线程
//                executorService.submit(new Runnable() {
//                    @Override
//                    public void run() {
//                        //当前线程名称
//                        String name = Thread.currentThread().getName();
//                        logger.info("当前线程：{}----统计添加数据中。。。。", name);
//                        List<Scada> scadaList = entity.getValue();
//
////                double average = scadaList.stream().mapToLong(value -> Long.parseLong(value.getValue())).average().getAsDouble();
////                double max = scadaList.stream().mapToLong(value -> Long.parseLong(value.getValue())).max().getAsLong();
////                double min = scadaList.stream().mapToLong(value -> Long.parseLong(value.getValue())).min().getAsLong();
////                double sum = scadaList.stream().mapToLong(value -> Long.parseLong(value.getValue())).sum();
//                        BigDecimal sum = BigDecimal.ZERO;
//                        BigDecimal average = BigDecimal.ZERO;
//                        BigDecimal maxVal = new BigDecimal(scadaList.get(0).getValue());
//                        BigDecimal minVal = new BigDecimal(scadaList.get(0).getValue());
//                        for (Scada scada : scadaList) {
//                            BigDecimal decimal = new BigDecimal(scada.getValue());
//                            sum = decimal.add(sum);
//                            if (decimal.compareTo(maxVal) > 0) {
//                                maxVal = decimal;
//                            }
//                            if (decimal.compareTo(minVal) < 0) {
//                                minVal = decimal;
//                            }
//                        }
//                        average = sum.divide(BigDecimal.valueOf(scadaList.size()), RoundingMode.HALF_UP);
//
//                        StatisticsScada statisticsScada = new StatisticsScada();
//                        statisticsScada.setCode(scadaList.get(0).getCode());
//                        statisticsScada.setValue(scadaList.get(scadaList.size() - 1).getValue());
//                        statisticsScada.setAve(average.toString());
//                        statisticsScada.setMax(maxVal.toString());
//                        statisticsScada.setMin(minVal.toString());
//                        statisticsScada.setFold(sum.toString());
//                        statisticsScada.setStartTime(startTime);
//                        statisticsScada.setEndTime(endTime);
//                        statisticsScada.setStatisticsType(DateType);
//
//                        //查询指标报警上下限计算合格率相关
//                        QueryWrapper<Allocation> allocationQueryWrapper = new QueryWrapper<>();
//                        allocationQueryWrapper.eq("VARIABLE_CODE", scadaList.get(0).getCode());
//                        List<Allocation> allocations = allocationMapper.selectList(allocationQueryWrapper);
//                        if (!CollectionUtils.isEmpty(allocations) && StringUtils.hasText(allocations.get(0).getType())) {
//                            Set<Long> reportIdSets = getReportIdSets(allocations);
//                            if (!CollectionUtils.isEmpty(reportIdSets)) {
//                                logger.info("计算指标报警上下限计算合格率相关....");
//                                QueryWrapper<ScadaReport> scadaReportQueryWrapper = new QueryWrapper<>();
//                                scadaReportQueryWrapper.in("ID", reportIdSets);
//                                List<ScadaReport> scadaReports = reportMapper.selectList(scadaReportQueryWrapper);
//                                if (!CollectionUtils.isEmpty(scadaReports)) {
//                                    //超标次数
//                                    int errorCount = 0;
//                                    //不合格超标时长
//                                    long errorTimeTotal = 0;
//                                    //超标总时长
//                                    long timeTotal = (scadaList.get(scadaList.size() - 1).getScadaTime().getTime()
//                                            - scadaList.get(0).getScadaTime().getTime()) / 1000;
//                                    //错误时间
//                                    Date errorTime = null;
//                                    for (Scada scada : scadaList) {
//                                        for (ScadaReport report : scadaReports) {
//                                            if (Double.parseDouble(scada.getValue()) < Double.parseDouble(report.getReportLower())
//                                                    || Double.parseDouble(scada.getValue()) > Double.parseDouble(report.getReportUpper())) {
//                                                errorCount = errorCount + 1;
//                                                errorTime = scada.getScadaTime();
//                                                break;
//                                            } else if (errorTime != null) {
//                                                errorTimeTotal = errorTimeTotal +
//                                                        (scada.getScadaTime().getTime() - errorTime.getTime()) / 1000;
//                                            }
//                                        }
//                                    }
//                                    //合格率
//                                    float pass = 0;
//                                    if (timeTotal != 0) {
//                                        BigDecimal num1 = new BigDecimal(String.valueOf(errorTimeTotal));
//                                        BigDecimal num2 = new BigDecimal(String.valueOf(timeTotal));
//                                        pass = num1.divide(num2, RoundingMode.HALF_UP).floatValue();
//                                    }
//
//                                    statisticsScada.setErrorCount(String.valueOf(errorCount));
//                                    statisticsScada.setErrorTimeTotal(String.valueOf(errorTimeTotal == 0 ? timeTotal : errorTimeTotal));
//                                    statisticsScada.setPass(String.valueOf(pass));
//                                    statisticsScada.setTimeTotal(String.valueOf(timeTotal));
//                                }
//                            }
//                            statisticsScada.setDataType(String.valueOf(allocations.get(0).getType()));
//                        }
//
//                        //删除旧数据
//
//                        int delete = statisticsScadaService.delete(DateType, statisticsScada.getCode(),
//                                DateUtil.format(statisticsScada.getStartTime(), "yyyy-MM-dd HH:mm:ss"),
//                                DateUtil.format(statisticsScada.getEndTime(), "yyyy-MM-dd HH:mm:ss"));
//                        logger.info("成功删除{}条旧数据", delete);
//                        //添加数据
//                        logger.info("统计添加数据完成：{}", statisticsScada);
//                        try {
//                            statisticsScadaService.save(statisticsScada);
//                        } catch (Exception e) {
//                            e.printStackTrace();
//                            log.error("统计数据添加出现错误----数据：{}---错误:{}", statisticsScada, e.getMessage());
//                        }
//                        logger.info("统计添加数据完成：{}", statisticsScada);
//                        // 闭锁
//                        countDownLatch.countDown();
//                    }
//                });
//            }
//        } else {
//            logger.info("获取历史数据为空");
//        }
    }


    /**
     * 截取数组长度批量插入
     *
     * @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;
    }


    /**
     * 分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" : k.getMaxValue();
                statisticsScada.setValue(maxValue);
                statisticsScada.setAve(k.getAvgValue());
                statisticsScada.setMin(k.getMinValue() == null ? "0" : k.getMinValue());
                statisticsScada.setMax(maxValue);
                statisticsScada.setFold(k.getDifferenceValue());
                List<Date> dates = getDateListByType(getDateType(k.getDateTimeStr(), dateType), dateType);
                if (!CollectionUtils.isEmpty(dates) && dates.size() == 2) {
                    statisticsScada.setStartTime(dates.get(0));
                    statisticsScada.setEndTime(dates.get(1));
                }
                statisticsScada.setStatisticsType(dateType);
                statisticsScada.setDataType(k.getAllocationType());
                //添加数据
                saveList.add(statisticsScada);
            });
            // 新增
            logger.info("新增scada数据大小{}", saveList.size());
            if (CollUtil.isNotEmpty(saveList)) {
                saveList.forEach(info -> statisticsScadaService.save(info));
            }


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

    /**
     * 获取时间的小时，天，月份的开始时间和结束时间
     *
     * @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<>();
        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);
        }
        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;
    }

    @Async
    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);
        }
    }


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

    @Override
    public boolean fetch24HoursData(String syncTime) {
        Date date = null;
        if (StringUtils.isEmpty(syncTime)) {
            date = new Date();
            Calendar cal = Calendar.getInstance();
            cal.setTime(date);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
        } else {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            try {
                Date parse = format.parse(syncTime);
                Date now = new Date();
                Calendar cal = Calendar.getInstance();
                cal.setTime(now);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                if (parse.getTime() >= now.getTime()) {
                    throw new RuntimeException("时间参数不允许大于等于当前时间：" + format.format(now));
                }
                date = parse;
            } catch (ParseException e) {
                throw new RuntimeException("时间参数格式传入错误：" + e);
            }
        }
//        insert24H(null, date);
        return true;
    }

    @Override
    public void fetchMonthsData(String syncTime) {
//        Date date = null;
//        if (StringUtils.isEmpty(syncTime)) {
//            date = new Date();
//            Calendar cal = Calendar.getInstance();
//            cal.setTime(date);
//            cal.set(Calendar.MINUTE, 0);
//            cal.set(Calendar.SECOND, 0);
//        } else {
//            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
//            try {
//                Date parse = format.parse(syncTime);
//                Date now = new Date();
//                Calendar cal = Calendar.getInstance();
//                cal.setTime(now);
//                if (parse.getTime() > now.getTime()) {
//                    throw new RuntimeException("时间参数不允许大于当前时间：" + format.format(now));
//                }
//                date = parse;
//            } catch (ParseException e) {
//                throw new RuntimeException("时间参数格式传入错误：" + e);
//            }
//        }
//        syncDataIntoMonths(null, date);
    }

    @Override
    public void fetchDaysData(String syncTime) {
//        Date date = null;
//        if (StringUtils.isEmpty(syncTime)) {
//            date = new Date();
//            Calendar cal = Calendar.getInstance();
//            cal.setTime(date);
//            cal.set(Calendar.MINUTE, 0);
//            cal.set(Calendar.SECOND, 0);
//        } else {
//            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
//            try {
//                Date parse = format.parse(syncTime);
//                Date now = new Date();
//                Calendar cal = Calendar.getInstance();
//                cal.setTime(now);
//                if (parse.getTime() > now.getTime()) {
//                    throw new RuntimeException("时间参数不允许大于当前时间：" + format.format(now));
//                }
//                date = parse;
//            } catch (ParseException e) {
//                throw new RuntimeException("时间参数格式传入错误：" + e);
//            }
//        }
//        syncDaysData(null, date);
    }

    @Override
    public Page<ScadaVo> get24HoursData(Page page, ScadaQuery scadaQuery) {
        QueryWrapper<ScadaQuery> queryWrapper = new QueryWrapper<>();
        if (StringUtils.hasText(scadaQuery.getCode())) {
            queryWrapper.like("hours.CODE", scadaQuery.getCode());
        }
        if (StringUtils.hasText(scadaQuery.getValue())) {
            queryWrapper.like("hours.VALUE", scadaQuery.getValue());
        }
        if (scadaQuery.getStart() != null && scadaQuery.getEnd() != null) {
            queryWrapper.between("hours.SCADA_TIME", scadaQuery.getStart(), scadaQuery.getEnd());
        }
        queryWrapper.orderByDesc("hours.SCADA_TIME");
        return scadaMapper.pageList(page, queryWrapper, "SCADA_HOURS");
    }


    @Override
    public Page getMonthsData(Page page, ScadaQuery scadaQuery) {
        QueryWrapper<ScadaQuery> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("hours.SCADA_TIME");
        if (StringUtils.hasText(scadaQuery.getCode())) {
            queryWrapper.like("hours.CODE", scadaQuery.getCode());
        }
        if (StringUtils.hasText(scadaQuery.getValue())) {
            queryWrapper.like("hours.VALUE", scadaQuery.getValue());
        }
        if (scadaQuery.getStart() != null && scadaQuery.getEnd() != null) {

            if (scadaQuery.getStart().getYear() == scadaQuery.getEnd().getYear() &&
                    scadaQuery.getStart().getMonth() == scadaQuery.getEnd().getMonth()) {
                List<Date> startAndEndByDate = TimeUtils.getMonthsToStartAndEndByDate(scadaQuery.getStart());
                queryWrapper.between("hours.SCADA_TIME", scadaQuery.getStart(), scadaQuery.getEnd());

                Calendar cal = Calendar.getInstance();
                cal.setTime(startAndEndByDate.get(0));
                SimpleDateFormat format = new SimpleDateFormat("yyyy_MM");
                String date = format.format(cal.getTime());
                String tableName = "SCADA_MONTH_" + date;
                return scadaMapper.pageList(page, queryWrapper, tableName);
            }
        }
        return scadaMapper.pageList(page, queryWrapper, SCADA_HISTORY);
    }

    @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(new QueryWrapper<Allocation>().lambda().eq(StringUtil.isNotNull(deviceQuery.getIsDisplay()),Allocation::getIsDisplay,deviceQuery.getIsDisplay()));
        //查询所有报警
        List<ScadaReport> scadaReports = reportMapper.selectList(null);
        //查询最新scada数据
        List<Scada> scadas = getListData();

        List<DeviceArchiveManageVo> manageVos = deviceArchiveManages.stream().map(deviceArchiveManage -> {
            DeviceArchiveManageVo vo = new DeviceArchiveManageVo();
            BeanUtils.copyProperties(deviceArchiveManage, vo);
            //查询绑定的指标
            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);
                            allocationVo.setScada(scadaList.get(0));
                        }
                    }
                    //设置报警相关信息
                    Set<Long> reportIdSets = getReportIdSets(Collections.singletonList(allocation));
                    if (!CollectionUtils.isEmpty(scadaReports) && !CollectionUtils.isEmpty(reportIdSets)) {
                        //筛选出报警信息列表
                        List<ScadaReport> reports = scadaReports.stream().filter(scadaReport -> scadaReport.getId() != null
                                && reportIdSets.contains(scadaReport.getId())).collect(Collectors.toList());
                        //设置指标报警状态为未报警
                        allocationVo.setIsAlarm("0");
                        //判断指标是否报警
                        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(lower) >= 0 && value.compareTo(upper) <= 0){
                                    allocationVo.setIsAlarm("1");
                                    break;
                                }
                            }
                        }
                        allocationVo.setScadaReports(reports);
                    }
                    return allocationVo;
                }).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 (vo.getDeviceType().equals(s)) {
                        return vo;
                    }
                }
                return null;
            }
            return vo;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        return manageVos;
    }

    /**
     * 获取关联指标
     *
     * @param vo
     */
    private void getAllocation(DeviceArchiveManageVo vo) {
        QueryWrapper<Allocation> allocationQueryWrapper = new QueryWrapper<>();
        allocationQueryWrapper.eq("DEVICE_ID", vo.getId());
        List<Allocation> allocations = allocationMapper.selectList(allocationQueryWrapper);
        if (!CollectionUtils.isEmpty(allocations)) {
            List<AllocationVo> allocationVos = allocations.stream().map(allocation -> {
                AllocationVo allocationVo = new AllocationVo();
                BeanUtils.copyProperties(allocation, allocationVo);
                //查询scada数据
                List<Scada> dataByCode = scadaMapper.getListDataByCode(allocation.getVariableCode());
                if (!CollectionUtils.isEmpty(dataByCode)) {
                    allocationVo.setScada(dataByCode.get(0));
                }
                //查询绑定的报警列表
                Set<Long> reportIdSets = getReportIdSets(Collections.singletonList(allocation));
                if (!CollectionUtils.isEmpty(reportIdSets)) {
                    logger.info("获取指标报警相关....");
                    QueryWrapper<ScadaReport> scadaReportQueryWrapper = new QueryWrapper<>();
                    scadaReportQueryWrapper.in("ID", reportIdSets);
                    List<ScadaReport> scadaReports = reportMapper.selectList(scadaReportQueryWrapper);
                    if (!CollectionUtils.isEmpty(scadaReports)) {
                        allocationVo.setScadaReports(scadaReports);
                    }
                }
                //返回数据
                return allocationVo;
            }).collect(Collectors.toList());
            vo.setAllocations(allocationVos);
        }
    }


    /**
     * 设置查询条件
     *
     * @param deviceQuery
     * @return
     */
    private QueryWrapper<DeviceArchiveManage> getQueryWrapper(DeviceQuery deviceQuery) {
        QueryWrapper<DeviceArchiveManage> queryWrapper = new QueryWrapper<>();
        if (!ObjectUtils.isEmpty(deviceQuery)) {
            if (StringUtils.hasText(deviceQuery.getType())) {
                queryWrapper.eq("manage.TYPE", deviceQuery.getType());
            }
            if (StringUtils.hasText(deviceQuery.getDeviceType())) {
                queryWrapper.eq("manage.DEVICE_TYPE", deviceQuery.getDeviceType());
            }
            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.DESCRIBE", 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 (deviceQuery.getLatitude() != null && deviceQuery.getLongitude() != null) {
                queryWrapper.eq("manage.LATITUDE", deviceQuery.getLatitude())
                        .eq("manage.LONGITUDE", deviceQuery.getLongitude());
            }
        }
        return queryWrapper;
    }


//    @Override
//    public void insertDataAndMouth(Scada scada) {
//        if (ObjectUtils.isEmpty(scada)) {
//            throw new ToflyDeniedException("数据为空");
//        }
//        if (scada.getId() != null && StringUtils.isEmpty(scada.getValue()) && scada.getScadaTime() == null) {
//            scada = getById(scada.getId());
//        }
//        log.info("获取到同步scada信息数据：-----{}-----", scada.toString());
//        if (!ObjectUtils.isEmpty(scada)) {
//            //存储到分表月份
//            String tableName = createMonths(scada.getScadaTime() == null ? new Date() : scada.getScadaTime());
//            log.info("创建月份表成功：-----{}-----", tableName);
//            //检测数据是否存在，不存在就插入
//            if (CollectionUtils.isEmpty(scadaMapper.selectHistoryById(tableName, scada.getId()))) {
//                log.info("scada数据不存在月份表中，正在执行新增数据");
//                scadaMapper.insertByOne(scada, tableName);
//                log.info("新增数据成功：-----{}-----", scada);
//            }
//            //检测报警信息
//            //sendNotice(scada);
//        }
//    }

    @Override
    @Async
    public void insertData(Scada scada) {
        if (ObjectUtils.isEmpty(scada)) {
            throw new ToflyDeniedException("数据为空");
        }

        if (!ObjectUtils.isEmpty(scada)) {
            //存储到分表月份
//            String tableName = createMonths(scada.getScadaTime() == null ? new Date() : scada.getScadaTime());
//            log.info("创建月份表成功：-----{}-----", tableName);
//            //检测数据是否存在，不存在就插入
//            if (CollectionUtils.isEmpty(scadaMapper.selectHistoryById(tableName, scada.getId()))) {
//                log.info("scada数据不存在月份表中，正在执行新增数据");
//                scadaMapper.insertByOne(scada, tableName);
//                log.info("新增数据成功：-----{}-----", scada);
//            }
            //检测报警信息
            sendNotice(scada);
        }
    }

    /**
     * 获取实时表SCADA_MONITORING_REALTIME中的数据根据ID
     *
     * @param scada
     * @return
     */
    private Scada getScada(Scada scada) {
        if (scada != null && scada.getId() != null && StringUtils.isEmpty(scada.getValue()) && scada.getScadaTime() == null) {
            List<Scada> scadas = baseMapper.selectScadaMonitoringRealtimeById("SCADA_MONITORING_REALTIME", scada.getId());
            if (!CollectionUtils.isEmpty(scadas)) {
                scada = scadas.get(0);
            }
        }
        return scada;
    }

    /**
     * 定时任务，每分钟1执行一次
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    @Async
    public void timingInsertData() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        Date end = calendar.getTime();
        calendar.add(Calendar.MINUTE, -2);
        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::getScadaTime, start, end));
//        log.info("时间{}-----{}----->查询每分钟定时任务实时数据{}", start, end, scadas);
        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, DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            }
        }
    }

    /**
     * 截取数组长度批量插入
     *
     * @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;
    }

    @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历史数据开始");
        switch (type) {
            case 0: {
                log.info("开始进行统计计算-----{}", type);
                insert24H(code, startTime, endTime, type);
                long e = System.currentTimeMillis();
                logger.info("统计计算小时结束,用时{}", e - s);
                break;
            }
            case 1: {
                log.info("开始进行统计计算-----{}", type);
                syncDaysData(code, startTime, endTime, type);
                long e = System.currentTimeMillis();
                logger.info("统计计算天结束,用时{}", e - s);
                break;
            }
            case 2: {
                log.info("开始进行统计计算-----{}", type);
                syncDataIntoMonths(code, startTime, endTime, type);
                long e = System.currentTimeMillis();
                logger.info("统计计算月结束,用时{}", e - s);
                break;
            }
            case 3: {
                //TODO 暂时不同步年数据
//                List<Date> dates = findDates(startTime, endTime,0);
//                if (!CollectionUtils.isEmpty(dates)){
//                    for (Date date : dates) {
//                        syncDaysData(date);
//                    }
//                }
                break;
            }
        }
    }

    @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;
        }
    }

    /**
     * 特殊值处理
     *
     * @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);
            }
        }
    }

    @Override
    public void fetchDataMouth(String code, String startStr, String endStr) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        Date end = DateUtil.parseDateTime(endStr);
        calendar.add(Calendar.MINUTE, -1);
        Date start = DateUtil.parseDateTime(startStr);

        List<String> codes = getCodes(code);
        if (CollectionUtils.isEmpty(codes)) {
            return;
        }
        List<Scada> scadas = scadaMapper.selectList(new QueryWrapper<Scada>().lambda()
                .between(Scada::getScadaTime, start, end)
                .in(Scada::getCode, codes));
        log.info("查询每分钟定时任务实时数据{}", scadas);
        if (!CollectionUtils.isEmpty(scadas)) {
            for (Scada scada : scadas) {
                //实时监控表
                if (scada.getValue().startsWith(".")) {
                    scada.setValue("0" + scada.getValue());
                }
                Scada monitoring = scadaMapper.selectDataByMonitoring(scada.getCode()).get(0);
                log.info("查询存在的实时数据{}", monitoring);
                if (ObjectUtils.isEmpty(monitoring)) {
                    scadaMapper.insertMonitoring(scada);
                    log.info("无实时数据，进行新增{}", scada);
                } else {
                    scadaMapper.updateMonitoring(scada);
                    log.info("有实时数据，进行更新{}", scada);
                }
                //月份表-警告通知
                insertData(scada);
            }
        }
    }


    /**
     * 获取日期列表
     *
     * @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;
    }


    /**
     * 异步执行同步信息
     *
     * @param data
     */
    public void syncSave(Scada data) {
        if (ObjectUtils.isEmpty(data)) {
            throw new RuntimeException("参数为空");
        }
        System.out.println("主线程 =====> 开始 =====> " + System.currentTimeMillis());

        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(() -> {
            System.out.println("异步线程 =====> 开始 =====> " + System.currentTimeMillis());
            try {
                Thread.sleep(5000);
                syncScadaData(data);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("异步线程 =====> 结束 =====> " + System.currentTimeMillis());
        });
        executorService.shutdown(); // 回收线程池

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("主线程 =====> 结束 =====> " + System.currentTimeMillis());

    }

    private void syncScadaData(Scada scada) {
//        Scada scada = null;
//        if (data.getId() != null) {
//            scada = scadaMapper.selectById(data.getId());
//        } else if (StringUtils.hasText(data.getCode())) {
//            Page<Scada> page = new Page<>(1, 1);
//            QueryWrapper<Scada> scadaQueryWrapper = new QueryWrapper<>();
//            scadaQueryWrapper.eq("CODE", data.getCode())
//                    .orderByDesc("SCADA_TIME");
//            Page<Scada> scadas = scadaMapper.selectPage(page, scadaQueryWrapper);
//            List<Scada> records = scadas.getRecords();
//            if (!CollectionUtils.isEmpty(records)) {
//                scada = records.get(0);
//            }
//        }

        log.info("获取到同步scada信息数据：-----{}-----", scada.toString());
        if (!ObjectUtils.isEmpty(scada)) {
            //检测是否为系统创建的指标
            log.info("检测是否为系统创建的指标");
            Integer selectCount = allocationMapper.selectCount(new QueryWrapper<Allocation>()
                    .eq("VARIABLE_CODE", scada.getCode()));
            if (selectCount == 0) {
                log.info("数据不存在指标绑定配置，无法进行同步更新存储");
                throw new RuntimeException("数据不存在指标绑定配置，无法进行同步更新存储");
            }
            //存储到历史表
            scadaMapper.insert(scada);
            //存储到分表月份
            String tableName = createMonths(scada.getScadaTime() == null ? new Date() : scada.getScadaTime());
            log.info("创建月份表成功：-----{}-----", tableName);
            //检测数据是否存在，不存在就插入
            if (CollectionUtils.isEmpty(scadaMapper.selectHistoryById(tableName, scada.getId()))) {
                log.info("scada数据不存在月份表中，正在执行新增数据");
                scadaMapper.insertByOne(scada, tableName);
                log.info("新增数据成功：-----{}-----", scada);
            }
            //检测报警信息
            sendNotice(scada);
        }
    }

    /**
     * 发送报警信息
     *
     * @param scada scada数据
     */
    @SneakyThrows
    @Async
    void sendNotice(Scada scada) {
        Thread.sleep(5000);
        scada = getScada(scada);
        log.info("获取到同步scada信息数据：-----{}-----", scada.toString());

        if (!StringUtils.hasText(scada.getValue())) {
            logger.info("数据：{}，存在value值为空，报警流程结束", scada);
            return;
        }
        QueryWrapper<Allocation> allocationQueryWrapper = new QueryWrapper<>();
        allocationQueryWrapper.eq("VARIABLE_CODE", scada.getCode())
                .eq("IS_ALARM", "1");
        List<Allocation> allocations = allocationMapper.selectList(allocationQueryWrapper);
        if (!CollectionUtils.isEmpty(allocations)) {
            for (Allocation allocation : allocations) {
                if (!StringUtils.hasText(allocation.getIsAlarm()) || allocation.getIsAlarm().equals("0")) {
                    logger.info("未设置报警开关，正在退出这次匹配");
                    break;
                }

                List<Long> reports = getReportIds(Collections.singletonList(allocation));
                QueryWrapper<ScadaReport> scadaReportQueryWrapper = new QueryWrapper<>();
                scadaReportQueryWrapper.eq("IS_DEL", FlowConstant.SHORT_ZERO)
                        .in("ID", reports);
                List<ScadaReport> scadaReports = reportMapper.selectList(scadaReportQueryWrapper);
                if (!CollectionUtils.isEmpty(scadaReports)) {
                    logger.info("匹配到告警模版...");
                    //报警值
                    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) {
                            MessagepushW messagepush = new MessagepushW();
                            // TODO 报警模版后续处理
                            messagepush.setTopic(MessageTopicEnum.REPORT_MSG.getTopic());
                            messagepush.setMessage(createMsg(scadaReport.getMsgTemplate(), scadaReport));
                            messagepush.setType("8");
                            messagepush.setTableName(MessageTopicEnum.REPORT_MSG.getTableName());
                            messagepush.setCreateTime(scada.getScadaTime());
                            sendTidingsUtil.sendMsg(messagepush, scadaReport.getLiaisonPeople());
                            logger.info("发送报警消息：{}", messagepush);
                            //保存报警信息列表数据
                            saveReportListData(scada, scadaReports, allocations);
                            logger.info("记录到报警列表：{}", scada);
                        }
                    }
                    log.info("--------报警处理结束----------");
                } else {
                    log.info("--------未匹配到报警模版---报警处理结束-------");
                }
            }
        }
    }


    /**
     * 发送报警信息（通过定时任务获取的数据）
     *
     * @param scada scada数据
     */
    @SneakyThrows
    @Async
    void sendNotice(Scada scada, String date) {
        if (!StringUtils.hasText(scada.getValue())) {
            logger.info("数据：{}，存在value值为空，报警流程结束", scada);
            return;
        }
        QueryWrapper<Allocation> allocationQueryWrapper = new QueryWrapper<>();
        allocationQueryWrapper.eq("VARIABLE_CODE", scada.getCode())
                .eq("IS_ALARM", "1");
        List<Allocation> allocations = allocationMapper.selectList(allocationQueryWrapper);
        if (!CollectionUtils.isEmpty(allocations)) {
            for (Allocation allocation : allocations) {
                if (!StringUtils.hasText(allocation.getIsAlarm()) || allocation.getIsAlarm().equals("0")) {
                    logger.info("未设置报警开关，正在退出这次匹配");
                    break;
                }

                List<Long> reports = getReportIds(Collections.singletonList(allocation));
                QueryWrapper<ScadaReport> scadaReportQueryWrapper = new QueryWrapper<>();
                scadaReportQueryWrapper.eq("IS_DEL", FlowConstant.SHORT_ZERO)
                        .in("ID", reports);
                List<ScadaReport> scadaReports = reportMapper.selectList(scadaReportQueryWrapper);
                if (!CollectionUtils.isEmpty(scadaReports)) {
                    //报警值
                    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) {
                            MessagepushW messagepush = new MessagepushW();
                            // TODO 报警模版后续处理
                            messagepush.setTopic(MessageTopicEnum.REPORT_MSG.getTopic());
                            messagepush.setMessage(createMsg(scadaReport.getMsgTemplate(), scadaReport));
                            messagepush.setType("8");
                            messagepush.setTableName(MessageTopicEnum.REPORT_MSG.getTableName());
                            messagepush.setCreateTime(scada.getScadaTime());
                            sendTidingsUtil.sendMsg(messagepush, scadaReport.getLiaisonPeople());
                            logger.info("发送报警消息：{}", messagepush);
                            //保存报警信息列表数据
                            saveReportListData(scada, scadaReports, allocations);

                        }
                    }
                    log.info("--------报警处理结束----------");
                } else {
                    log.info("--------未匹配到报警模版---报警处理结束-------");
                }
            }
        }
    }

    private void saveReportListData(Scada scada, List<ScadaReport> scadaReports, List<Allocation> allocations) {
        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);
            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);
            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 = 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 = org.apache.commons.lang3.StringUtils.join(displayNames);
            tfScadaReportList.setDisplayName(join);
        }
        tfScadaReportList.setStatus("0");
        tfScadaReportListService.insert(tfScadaReportList);
        log.info("添加列表报警数据成功：{}", tfScadaReportList);
    }


    /**
     * 构建信息内容模版
     *
     * @param template
     * @param scadaReport
     * @return
     */
    public String createMsg(String template, ScadaReport scadaReport) {
        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(user, "#YHXM#");
        }
        if (template.contains("#LXDH#")) {
            template = template.replace(scadaReport.getLiaisonPhone(), "#LXDH#");
        }
        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;
    }

    /**
     * 获取报警ID集合(去重)
     *
     * @param allocations
     * @return
     */
    public static Set<Long> getReportIdSets(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;
    }

    @Scheduled(cron = "0 0 0/1 * * ? ")
    @Transactional(rollbackFor = Exception.class)
    public void automaticSendNetCumulativeFlow() {
        log.info("定时同步scada净累计数据开启......");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.HOUR, -1);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        Date endTime = calendar.getTime();
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date startTime = calendar.getTime();
        sendNetCumulativeFlow(startTime, endTime);
    }

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

    }

    private void sendNetCumulativeFlow(Date sDate, Date eDate) {
        List<Scada> scadaList = new ArrayList<>();
        // 获取时间范围内指标类型为正反17,18的监测数据
        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, ".JLJ"))
                .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("17") && i.getValue() != null) {
                        cug.updateAndGet(v1 -> v1 + Double.parseDouble(i.getValue()));
                    }
                    if (i.getIndexType().equals("18") && 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("17")) {
                    allocation = allocationMap.get("17");
                } else {
                    allocation = allocationMap.get("18");
                }
                allocation.setId(null);
                allocation.setVariableCode(allocation.getVariableCode() + ".JLJ");
                allocation.setVariableName(allocation.getVariableName() + ".净累计");
                allocation.setDisplayName(allocation.getDisplayName() + ".净累计");

                allocation.setType("19");
                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 str
     * @return
     */
    public static boolean isNumber(String str) {
        Matcher m = pattern.matcher(str);
        return m.matches();
    }


}
