AiTaskService
package com.ruoyi.pc.service;

import com.ruoyi.pc.domain.*;

import java.util.List;

public interface AiTaskService {

    /**
     * 处理消息
     *
     * @param message
     */
    public void processMessage(String message);

    public void saveMonitoringDeviceResult(String createDate, List<ModelResult> modelResults, String frameImgPath, PcMonitoringDevice pcMonitoringDevice, PcMonitoringDeviceModel monitoringDeviceModel);

    public void publishAlarmInformation(PcMonitoringDeviceResult monitoringDeviceResult, String deptName, String aiModelName, String monitoringDeviceName);

    CheckResultAlarmRule checkResultRule(PcMonitoringDeviceModel pcMonitoringDeviceModel,List<ModelResult> modelResults);
}

public class AiTaskServiceImpl implements AiTaskService
package com.ruoyi.pc.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.config.ServerConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.websocket.WebSocketServer;
import com.ruoyi.pc.config.DeviceMqtt;
import com.ruoyi.pc.config.redis.RedisUtil;
import com.ruoyi.pc.domain.*;
import com.ruoyi.pc.nodeserver.XbsNodeService;
import com.ruoyi.pc.service.*;
import com.ruoyi.pc.task.util.Location;
import com.ruoyi.pc.task.util.clustering.CentralPoint;
import com.ruoyi.pc.task.util.clustering.ClusteringUtils;
import com.ruoyi.pc.task.util.clustering.RectangleUtil;
import com.ruoyi.pc.util.*;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import net.sf.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * agent控制服务
 */
@Service("aiTaskService")
public class AiTaskServiceImpl implements AiTaskService {
    public static Logger logger = LoggerFactory.getLogger(AiTaskServiceImpl.class);

    Gson gson = new Gson();

    @Resource
    ThreadPoolTaskExecutor pool;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    ISysConfigService configService;
    @Autowired
    IPcMonitoringDeviceResultService iPcMonitoringDeviceResultService;
    @Autowired
    IPcAlarmConfigService pcAlarmConfigService;
    @Autowired
    IPcMonitoringDeviceVolumeService iPcMonitoringDeviceVolumeService;
    @Autowired
    AgentService agentService;
    @Autowired
    XbsNodeService xbsNodeService;
    @Autowired
    IPcPlayRecordService playRecordService;
    @Autowired
    IPcMonitoringTaskService taskService;

    @Resource
    private ISysDeptService sysDeptService;

    @Autowired
    DeRecognition deRecognition;

    @Resource
    private IPcDataReportConfigService pcDataReportConfigService;

    @Resource
    private ISysUserService userService;

    @Resource
    private WebSocketServer webSocketServer;

    @Autowired
    private ServerConfig serverConfig;

    /**
     * 处理消息
     *
     * @param message
     */
    public void processMessage(String message) {
        // 1、获取创建时间、图片地址、通道信息、关联模型信息、格式化后的识别结果
        AiMessage aiMessage = null;
        try {
            aiMessage = gson.fromJson(message, AiMessage.class);
        } catch (JsonSyntaxException e) {
            logger.error("格式转换错误" + e);
            return;
        }
        Long taskId = aiMessage.getTaskId();
        String createDate = aiMessage.getCreateDate();
        logger.info("任务ID--------" + taskId + ",发布时间--------" + createDate);
        String imgPath = aiMessage.getImgPath();
        PcMonitoringDevice pcMonitoringDevice = aiMessage.getPcMonitoringDevice();
        PcMonitoringDeviceModel pcMonitoringDeviceModel = aiMessage.getPcMonitoringDeviceModel();
        List<ModelResult> modelResults = aiMessage.getModelResults();
        List<ModelResult> coincideModelResults = new ArrayList<>();
        CheckResultAlarmRule checkResultAlarmRule = checkResultRule(pcMonitoringDeviceModel, modelResults);
        //3、判断当前通道当前模型是否可以报警
        if (checkResultAlarmRule.isAlarm()) {
            ArrayList<Location> locations = checkResultAlarmRule.getLocations();
            boolean flag = true;
            //获取当前模型的监控任务时长
            //判断是否开启去重
            if (pcMonitoringDeviceModel.getEnableDeduplication() != null && StringUtils.isNotBlank(pcMonitoringDeviceModel.getEnableDeduplication())
                    && pcMonitoringDeviceModel.getDeduplicationMode() != null && StringUtils.isNotBlank(pcMonitoringDeviceModel.getDeduplicationMode())
                    && pcMonitoringDeviceModel.getDeduplicationDuration() != null && StringUtils.isNotBlank(pcMonitoringDeviceModel.getDeduplicationDuration()))
                if (Objects.equals(pcMonitoringDeviceModel.getEnableDeduplication(), "0")) {
                    if (!(pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_2) && locations.size() == 0)) {
                        //进行告警去重,如果告警信息一致则直接过滤,不一致则进行监测时长判断
                        if (Objects.equals(pcMonitoringDeviceModel.getDeduplicationMode(), "0")) {
                            //过滤数量
                            flag = deRecognition.quantityDetection(taskId, pcMonitoringDeviceModel, coincideModelResults, pcMonitoringDeviceModel.getDeduplicationDuration());
                        } else if (Objects.equals(pcMonitoringDeviceModel.getDeduplicationMode(), "1")) {
                            //过滤坐标
                            flag = deRecognition.coordinateDetection(taskId, pcMonitoringDeviceModel, coincideModelResults, pcMonitoringDeviceModel.getDeduplicationDuration());
                        } else if (Objects.equals(pcMonitoringDeviceModel.getDeduplicationMode(), "2")) {
                            //去重识别
                            flag = deRecognition.deduplicationIdentification(taskId, pcMonitoringDeviceModel, pcMonitoringDeviceModel.getDeduplicationDuration());
                        }
                    }
                }
            if (!flag) {
                return;
            }
            //4、判断是否设置监测时长以及是否可以报警
            TimeThresholdInfo timeThresholdInfo = isTimeThreshold(createDate, taskId, locations, imgPath, pcMonitoringDeviceModel, modelResults);
            if (timeThresholdInfo.isFlag()) {
                //结果坐标
                ArrayList<Location> newLocations = timeThresholdInfo.getLocations();
                //模型框图坐标
                List<ModelResult> newModelResults = timeThresholdInfo.getModelResults();
                // 5、保存告警信息
                pool.execute(() -> {
                    SysDept sysDept = sysDeptService.selectDeptById(pcMonitoringDevice.getDeptId());
                    //生成水印内容
                    List<String> markContents = new ArrayList<String>();
                    markContents.add("模型名称:" + pcMonitoringDeviceModel.getAiModelName());
                    markContents.add("监控设备名称:" + pcMonitoringDevice.getName());
                    markContents.add("告警信息:" + pcMonitoringDeviceModel.getAlarmDetails());
                    markContents.add("告警时间:" + createDate);
                    markContents.add("部门名称:" + sysDept.getDeptName());
                    //5.1、根据坐标截图保存,并返回框图地址
                    String frameImgPath = transformImg(newLocations, timeThresholdInfo.getImgPath(), pcMonitoringDevice.getImei(), pcMonitoringDeviceModel, markContents);
                    if (null == frameImgPath || frameImgPath.trim().length() == 0) {
                        return;
                    }
                    //5.2、保存告警信息,包括框图访问地址
                    saveMonitoringDeviceResult(createDate, newModelResults, frameImgPath, pcMonitoringDevice, pcMonitoringDeviceModel);
                });
                //判断当前通道模型的识别结果是否进行播报:0:播报,1:不播报
                if (pcMonitoringDeviceModel.getSoundColumnAlarmStatus() != null && pcMonitoringDeviceModel.getSoundColumnAlarmStatus() == 1) {
                    return;
                }
                // 6、音箱播控
                pool.execute(new Runnable() {
                    @Override
                    public void run() {
                        //6.1、根据通道ID,获取关联音柱设备列表
                        List<PcDevice> pcList = iPcMonitoringDeviceVolumeService.selectPcDeviceListByMonitoringDeviceId(pcMonitoringDeviceModel.getMonitoringDeviceId());
                        //6.2、判断是否播放音箱设备,播放模型对应的音频文件
                        List<PcDevice> xbsPcList = new ArrayList<>(); //有线音柱设备集合
                        List<PcDevice> xxyPcList = new ArrayList<>(); //无线音柱设备集合
                        for (PcDevice pcDevice : pcList) {
                            if (null != pcDevice && pcDevice.getType().equals(URLConstant.DEVICE_TYPE_XBS)) {
                                xbsPcList.add(pcDevice);
                            }
                            if (null != pcDevice && pcDevice.getType().equals(URLConstant.DEVICE_TYPE_XXY)) {
                                xxyPcList.add(pcDevice);
                            }
                        }
                        //6.3.1、有线音柱将设备全部播放
                        if (xbsPcList.size() > 0) {
                            try {
                                //8.12.1、有线音柱将设备全部停止
                                xbsNodeService.stopPcDeviceByAep(xbsPcList);
                                //8.12.2、有线音柱开始播放设备
                                xbsNodeService.palyPcDeviceByAep(xbsPcList, pcMonitoringDeviceModel.getAudioUrl());
                            } catch (Exception e) {
                                logger.error("有线音柱播放失败" + e);
                            }
                        }
                        //6.3.2、无线音柱将设备全部播放
                        if (xxyPcList.size() > 0) {
                            try {
                                // 加入无线音柱操作列表
                                ArrayList<DeviceMqtt> devices = new ArrayList<DeviceMqtt>();
                                for (int i = 0; i < xxyPcList.size(); i++) {
                                    PcDevice pcDevice = xxyPcList.get(i);
                                    DeviceMqtt deviceMqtt = new DeviceMqtt(pcDevice.getAep());
                                    deviceMqtt.setTask_id("202101011200371234"); //默认
                                    deviceMqtt.setVol(Integer.valueOf(pcDevice.getVol()));
                                    devices.add(deviceMqtt);
                                }
                                //8.12、开始播放无线音柱
                                agentService.setPlayService(pcMonitoringDeviceModel.getAudioUrl(), URLConstant.PLAY_LEVEL_1, URLConstant.NORMAL_PLAY, devices);
                            } catch (Exception e) {
                                logger.error("无线音柱播放失败" + e);
                            }
                        }
                        //6.4、记录音箱设备播放记录
                        playRecordService.insertPcPlayRecord(pcMonitoringDevice.getCreateId(), pcMonitoringDevice.getDeptId(), pcList, URLConstant.TYPE_MONITORING);
                    }
                });
            }
        }
    }

    /**
     * 检测结果告警规则
     * @param pcMonitoringDeviceModel pcMonitoringDeviceModel
     * @param modelResults List<ModelResult>
     * @return CheckResultAlarmRule
     */
    public CheckResultAlarmRule checkResultRule(PcMonitoringDeviceModel pcMonitoringDeviceModel,List<ModelResult> modelResults){
        CheckResultAlarmRule checkResultAlarmRule = new CheckResultAlarmRule();
        List<ModelResult> coincideModelResults = new ArrayList<>();
        ArrayList<Location> locations = null;
        boolean isAlarm = false; //默认不保存告警信息
        // 2、 当模型比较类型是大于等于阈值时
        if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_0)) {
            // 2.1、获取模型识别结果与通道框选区域重合的识别结果列表
            coincideModelResults = getCoincideModelResults(modelResults, pcMonitoringDeviceModel);
            // 2.2、判断重合的模型识别结果列表长度是否大于等于告警目标数阈值
            if (coincideModelResults.size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber())) {
                // 2.3、若是,继续格式化模型识别结果列表为框图Location列表
                locations = getLocations(coincideModelResults);
                // 2.4、定制算法判断(聚集算法、其他算法)并更新框图Location列表
                // 2.4.1、聚集算法告警判断,默认告警
                //2.4.2、其他算法
                isAlarm = isGather(pcMonitoringDeviceModel, locations);
            }
        }
        if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_1)) {
            // 2.1、获取模型识别结果与通道框选区域重合的识别结果列表
            coincideModelResults = getCoincideModelResults(modelResults, pcMonitoringDeviceModel);
            // 2.2、判断重合的模型识别结果列表长度是否大于等于告警目标数阈值
            if (coincideModelResults.size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber())) {
                // 2.3、若是,继续格式化模型识别结果列表为框图Location列表
                locations = getLocations(coincideModelResults);
                // 2.4、定制算法判断(聚集算法、其他算法)并更新框图Location列表
                // 2.4.1、聚集算法告警判断,默认告警
                //2.4.2、其他算法
                isAlarm = isGather(pcMonitoringDeviceModel, locations);
            }
        }
        // 2、 当模型比较类型是小于等于阈值(包含识别没有结果)时
        if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_2)) {
            logger.info("-----------------------start-------------------{}", gson.toJson(modelResults));
            List<ModelResult> modelResultList = new ArrayList<>();
            for (ModelResult modelResult : modelResults) {
                if (modelResult.getBbox().size() > 0 && modelResult.getScore() > Double.parseDouble(pcMonitoringDeviceModel.getSimilarity())) {
                    modelResultList.add(modelResult);
                }
            }
            if (modelResultList.size() > 0) {
                Map<String, List<ModelResult>> coincideModelResultsType = new HashMap<>();
                JSONArray coordinates = JSONArray.fromObject(pcMonitoringDeviceModel.getCoordinate());
                if (coordinates.size() == 0) {
                    coincideModelResultsType.put("0", modelResultList);
                } else {
                    coincideModelResultsType = getCoincideModelResultsType(modelResultList, pcMonitoringDeviceModel);
                }
                logger.error("---------------coincideModelResultsType============{}----------------------", gson.toJson(coincideModelResultsType));
                boolean flag = false;
                String departure_model_type = configService.selectConfigByKey(URLConstant.DEPARTURE_MODEL_TYPE);
                if (CollectionUtil.isNotEmpty(coincideModelResultsType)) {
                    if (Objects.equals(departure_model_type, "0")) {
                        flag = coincideModelResultsType.entrySet().stream().allMatch(l -> l.getValue().size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber()));
                        logger.error("---------------coincideModelResultsType.entrySet().stream().allMatch(l -> l.getValue().size()============{}------pcMonitoringDeviceModel.getNumber()==================={}----------------{}", gson.toJson(coincideModelResultsType), Integer.parseInt(pcMonitoringDeviceModel.getNumber()), flag);
                    } else if (Objects.equals(departure_model_type, "1")) {
                        flag = coincideModelResultsType.entrySet().stream().anyMatch(l -> l.getValue().size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber()));
                        logger.error("---------------coincideModelResultsType.entrySet().stream().anyMatch(l -> l.getValue().size()============{}------pcMonitoringDeviceModel.getNumber()==================={}----------------{}", gson.toJson(coincideModelResultsType), Integer.parseInt(pcMonitoringDeviceModel.getNumber()), flag);
                    } else if (Objects.equals(departure_model_type, "2")) {
                        flag = coincideModelResultsType.values().stream().mapToInt(List::size).sum() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber());
                        logger.error("---------------coincideModelResultsType.values().stream().mapToInt(List::size).sum()============{}------pcMonitoringDeviceModel.getNumber()==================={}----------------{}", coincideModelResultsType.values().stream().mapToInt(List::size).sum(), Integer.parseInt(pcMonitoringDeviceModel.getNumber()), flag);
                    }
                }
                isAlarm = !flag;
            } else {
                isAlarm = true;
            }
            locations = new ArrayList<>();
            logger.info("-----------------------end-------------------");
        }
        checkResultAlarmRule.setAlarm(isAlarm);
        checkResultAlarmRule.setLocations(locations);
        checkResultAlarmRule.setModelResults(coincideModelResults);
        return checkResultAlarmRule;
    }

    /**
     * 获取模型识别结果与通道框选区域重合的识别结果列表
     *
     * @param modelResults
     * @param monitoringDeviceModel
     * @return
     */
    private List<ModelResult> getCoincideModelResults(List<ModelResult> modelResults, PcMonitoringDeviceModel monitoringDeviceModel) {

        List<ModelResult> coincideModelResults = new ArrayList<ModelResult>();
        // 1、判断是否标记了识别范围,若是,筛选识别结果和通道框选区域重合的识别结果数据集,若不是,识别所有
        // 2、获取识别区域框选范围坐标集合
        // coordinates: [{"oX":151,"oY":108},{"oX":417,"oY":130},{"oX":376,"oY":236},{"oX":196,"oY":294},{"oX":135,"oY":195}]
        JSONArray coordinates = JSONArray.fromObject(monitoringDeviceModel.getCoordinate());
//        logger.info("识别区域框选范围"+coordinates.toString());
        if (coordinates.size() > 0) {
            List<PolygonUtil.Polygon> polygonList = new ArrayList<>();
            for (Object coordinateArrays : coordinates) {
                JSONArray coordinateList = JSONArray.fromObject(coordinateArrays);
                if (coordinateList.size() > 0) {
                    ArrayList<PolygonUtil.Point> coordinatePoints = new ArrayList<PolygonUtil.Point>();
                    for (int i = 0; i < coordinateList.size(); i++) {
                        Coordinate coordinate = gson.fromJson(coordinateList.get(i).toString(), Coordinate.class);
                        coordinatePoints.add(new PolygonUtil.Point(coordinate.getoX(), coordinate.getoY()));
                    }
                    PolygonUtil.Polygon coordinatePolygon = new PolygonUtil.Polygon().setPoints(coordinatePoints);
                    polygonList.add(coordinatePolygon);
                }
            }
            // 3、 遍历识别结果集合,判断识别结果是否和通道框选区域重合,若是,添加jsonArray中
            // array: [{ "category": "smoke", "bbox": [182.2, 218.1, 211.2, 278.4],"score": 0.7568120956420898 },{ "category": "smoke", "bbox": [182.0, 218.0, 211.0, 278.0],"score": 0.7568120956420898 }]
            for (ModelResult modelResult : modelResults) {
                List<Double> bbox = modelResult.getBbox(); // 获取框图位置信息
                PolygonUtil.Polygon locationPolygon = new PolygonUtil.Polygon().setPoints(Arrays.asList(
                        new PolygonUtil.Point(bbox.get(0), bbox.get(1)),
                        new PolygonUtil.Point(bbox.get(2), bbox.get(1)),
                        new PolygonUtil.Point(bbox.get(2), bbox.get(3)),
                        new PolygonUtil.Point(bbox.get(0), bbox.get(3))
                ));
                //判断多边形是否相交
                for (PolygonUtil.Polygon polygonTest : polygonList) {
                    //判断多边形是否相交
//                    boolean coinCide = PolygonUtil.intersectionJudgment(locationPolygon, polygonTest);
                    String area = getAreaProportion(locationPolygon, polygonTest);
//                    String area = "";
                    logger.info("框图坐标->{},识别结果坐标->{},面积占比大小:{}", gson.toJson(polygonTest), gson.toJson(locationPolygon), area);
                    //判断设置是在框外还是框内 : 0:框内,1:框外
                    if (monitoringDeviceModel.getBoxStatus() == null || monitoringDeviceModel.getBoxStatus() == 0) {
                        //判断当前面积是否大于设置阈值
                        if (Double.parseDouble(area) > 0 && new BigDecimal(area).compareTo(new BigDecimal(StringUtils.isEmpty(monitoringDeviceModel.getAreaThreshold()) ? "0" : monitoringDeviceModel.getAreaThreshold())) >= 0) {
                            coincideModelResults.add(modelResult);
                            break;
                        }
                    } else {
                        if (new BigDecimal(area).compareTo(new BigDecimal(StringUtils.isEmpty(monitoringDeviceModel.getAreaThreshold()) ? "0" : monitoringDeviceModel.getAreaThreshold())) < 0) {
                            coincideModelResults.add(modelResult);
                            break;
                        }
                    }
                }
            }
        } else {
            coincideModelResults.addAll(modelResults);
        }
        return coincideModelResults;
    }

    @SuppressWarnings("all")
    private Map<String, List<ModelResult>> getCoincideModelResultsType(List<ModelResult> modelResults, PcMonitoringDeviceModel monitoringDeviceModel) {
        Map<String, List<ModelResult>> coincideModelResults = new HashMap<>();
        // 1、判断是否标记了识别范围,若是,筛选识别结果和通道框选区域重合的识别结果数据集,若不是,识别所有
        // 2、获取识别区域框选范围坐标集合
        // coordinates: [[{"oX":151,"oY":108},{"oX":417,"oY":130},{"oX":376,"oY":236},{"oX":196,"oY":294},{"oX":135,"oY":195}],[{"oX":151,"oY":108},{"oX":417,"oY":130},{"oX":376,"oY":236},{"oX":196,"oY":294},{"oX":135,"oY":195}]]
        JSONArray coordinates = JSONArray.fromObject(monitoringDeviceModel.getCoordinate());
        List<PolygonUtil.Polygon> polygonList = new ArrayList<>();
        for (Object coordinateArrays : coordinates) {
            JSONArray coordinateList = JSONArray.fromObject(coordinateArrays);
            if (coordinateList.size() > 0) {
                ArrayList<PolygonUtil.Point> coordinatePoints = new ArrayList<PolygonUtil.Point>();
                for (Object o : coordinateList) {
                    Coordinate coordinate = gson.fromJson(o.toString(), Coordinate.class);
                    coordinatePoints.add(new PolygonUtil.Point(coordinate.getoX(), coordinate.getoY()));
                }
                PolygonUtil.Polygon coordinatePolygon = new PolygonUtil.Polygon().setPoints(coordinatePoints);
                polygonList.add(coordinatePolygon);
            }
        }
        logger.info("----------------------start------------------+++++++++++++++++++++++++++++++++++++");
        for (ModelResult modelResult : modelResults) {
            List<Double> bbox = modelResult.getBbox(); // 获取框图位置信息
            PolygonUtil.Polygon locationPolygon = new PolygonUtil.Polygon().setPoints(Arrays.asList(
                    new PolygonUtil.Point(bbox.get(0), bbox.get(1)),
                    new PolygonUtil.Point(bbox.get(2), bbox.get(1)),
                    new PolygonUtil.Point(bbox.get(2), bbox.get(3)),
                    new PolygonUtil.Point(bbox.get(0), bbox.get(3))
            ));
            //判断多边形是否相交
            logger.info("-------polygonList.size()------{}------------", polygonList.size());
            for (int i = 0; i < polygonList.size(); i++) {
                List<ModelResult> modelResultList = coincideModelResults.get(i + "");
                if (CollectionUtil.isEmpty(modelResultList))
                    modelResultList = new ArrayList<>();
                //判断多边形是否相交
                String area = getAreaProportion(locationPolygon, polygonList.get(i));
                logger.info("************{}**********area-----{}*****", i, area);
                //判断设置是在框外还是框内 : 0:框内,1:框外
                if (monitoringDeviceModel.getBoxStatus() == null || monitoringDeviceModel.getBoxStatus() == 0) {
                    //判断当前面积是否大于设置阈值
                    if (Double.parseDouble(area) > 0 && new BigDecimal(area).compareTo(new BigDecimal(StringUtils.isEmpty(monitoringDeviceModel.getAreaThreshold()) ? "0" : monitoringDeviceModel.getAreaThreshold())) >= 0) {
                        logger.error("面积占比大小{}>面积占比阈值{}", area, monitoringDeviceModel.getAreaThreshold());
                        modelResultList.add(modelResult);
                        logger.info("----------modelResult---------{}", gson.toJson(modelResult));
                    }
                    coincideModelResults.put(i + "", modelResultList);
                } else {
                    if (new BigDecimal(area).compareTo(new BigDecimal(StringUtils.isEmpty(monitoringDeviceModel.getAreaThreshold()) ? "0" : monitoringDeviceModel.getAreaThreshold())) <= 0) {
                        modelResultList.add(modelResult);
                    }
                    coincideModelResults.put(i + "", modelResultList);
                }
            }
        }
        logger.info("----------------------end------------------+++++++++++++++++++++++++++++++++++++");
        return coincideModelResults;
    }

    public static String getAreaProportion(PolygonUtil.Polygon locationPolygon, PolygonUtil.Polygon polygonTest) {
        //框图坐标
        List<PolygonUtil.Point> polygonTestPoints = polygonTest.getPoints();
        //结果坐标
        List<PolygonUtil.Point> locationPolygonPoints = locationPolygon.getPoints();
        // 创建第一个多边形
        GeometryFactory factory = new GeometryFactory();

        com.vividsolutions.jts.geom.Coordinate[] coords1 = new com.vividsolutions.jts.geom.Coordinate[polygonTestPoints.size() + 1];
        for (int i = 0; i < polygonTestPoints.size(); i++) {
            coords1[i] = new com.vividsolutions.jts.geom.Coordinate(polygonTestPoints.get(i).getX(), polygonTestPoints.get(i).getY(), 0.0);
            if (polygonTestPoints.size() - 1 == i) {
                coords1[i + 1] = new com.vividsolutions.jts.geom.Coordinate(polygonTestPoints.get(0).getX(), polygonTestPoints.get(0).getY(), 0.0);
            }
        }
        com.vividsolutions.jts.geom.Polygon polygon1 = factory.createPolygon(coords1);

        com.vividsolutions.jts.geom.Coordinate[] coords2 = new com.vividsolutions.jts.geom.Coordinate[locationPolygonPoints.size() + 1];
        for (int i = 0; i < locationPolygonPoints.size(); i++) {
            coords2[i] = new com.vividsolutions.jts.geom.Coordinate(locationPolygonPoints.get(i).getX(), locationPolygonPoints.get(i).getY(), 0.0);
            if (locationPolygonPoints.size() - 1 == i) {
                coords2[i + 1] = new com.vividsolutions.jts.geom.Coordinate(locationPolygonPoints.get(0).getX(), locationPolygonPoints.get(0).getY(), 0.0);
            }
        }
        com.vividsolutions.jts.geom.Polygon polygon2 = factory.createPolygon(coords2);

        // 对第一个多边形进行自交处理
        Geometry polygon1Buffer = polygon1.buffer(0.01);
        Geometry polygon1BufferUnion = polygon1Buffer.union();

        // 计算两个多边形的相交面积
        Geometry intersection = polygon2.intersection(polygon1BufferUnion);

        // 计算相交面积占polygon1面积的百分比
        double percentage = intersection.getArea() / polygon2.getArea() * 100;

        return new Gson().toJson(percentage);
    }

    /**
     * 格式化模型识别结果列表为框图Location列表
     *
     * @param coincideModelResults
     */
    private ArrayList<Location> getLocations(List<ModelResult> coincideModelResults) {
        ArrayList<Location> locations = new ArrayList<>();
        for (ModelResult modelResult : coincideModelResults) {
            Location location = new Location();
            List<Double> bbox = modelResult.getBbox();
            location.setX(bbox.get(0).intValue());
            location.setY(bbox.get(1).intValue());
            Double width = bbox.get(2) - bbox.get(0);
            location.setWidth(width.intValue());
            Double height = bbox.get(3) - bbox.get(1);
            location.setHeight(height.intValue());
            locations.add(location);
        }
        return locations;
    }

    /**
     * 判断是否有聚集
     * K-Means算法计算聚集情况,true 聚集 false 没有聚集
     *
     * @param monitoringDeviceModel
     * @param locations
     * @return
     */
    private boolean isGather(PcMonitoringDeviceModel monitoringDeviceModel, ArrayList<Location> locations) {
        boolean isGather = true; //是否可以直接保存告警结果,默认可以直接告警
        String model_id_gather = configService.selectConfigByKey(URLConstant.MODEL_ID_GATHER); // 进行聚集算法判断的模型ID
        if (null != model_id_gather && model_id_gather.trim().length() > 0 && !model_id_gather.equals("model_id_gather")) {
            //若需要聚集算法判断的模型名称中包含当前模型名称
            boolean flag = false; // 默认当前模型不是聚集算法
            String[] split = model_id_gather.split(",");
            for (int i = 0; i < split.length; i++) {
                if (Long.parseLong(split[i]) == monitoringDeviceModel.getAiModelId()) {
                    flag = true; // 是聚集算法
                    break;
                }
            }
            //当前是聚集算法
            if (flag) {
                //获取矩形框的中心点数组
                ArrayList<CentralPoint> centralPoints = RectangleUtil.calculateCentralPoint(locations);
                // 将中心点坐标用double数组存储,方便聚类计算使用
                ArrayList<double[]> dataSet = new ArrayList<>();
                for (CentralPoint centralPoint : centralPoints) {
                    double[] onePoint = new double[2];
                    onePoint[0] = centralPoint.getX();
                    onePoint[1] = centralPoint.getY();
                    dataSet.add(onePoint);
                }
                //K-Means算法计算聚集情况 true 聚集 false 没有聚集
                isGather = ClusteringUtils.kMeansGather(dataSet, Integer.parseInt(monitoringDeviceModel.getNumber()));
            }

        }
        return isGather;
    }

    /**
     * 判断当前通道是否可以告警
     *
     * @param createDate
     * @param taskId
     * @param locations
     * @param imgPath
     * @param pcMonitoringDeviceModel
     * @return
     */
    private TimeThresholdInfo isTimeThreshold(String createDate, Long taskId, ArrayList<Location> locations, String imgPath, PcMonitoringDeviceModel pcMonitoringDeviceModel, List<ModelResult> modelResults) {
        TimeThresholdInfo timeThresholdInfo = null;
        String timeThreshold = pcMonitoringDeviceModel.getTimeThreshold();
        Integer monitoringStatus = pcMonitoringDeviceModel.getMonitoringStatus();
        if ((monitoringStatus == null || monitoringStatus == 0) && null != timeThreshold && timeThreshold.trim().length() != 0 && !timeThreshold.equals("0")) {
            //若不为空且不等于0
            //判断redis中当前通道、当前模型是否存在监测时长相关信息,若不存在,保存第一次监测的信息,监测信息包括,第一次记录时间,当前记录次数,报警信息
            String keyTimeThreshold = pcMonitoringDeviceModel.getMonitoringDeviceId() + "_" + pcMonitoringDeviceModel.getAiModelId() + "_" + pcMonitoringDeviceModel.getClassName() + "_" + pcMonitoringDeviceModel.getLabel() + "_time_threshold";
            if (!redisUtil.hasKey(keyTimeThreshold)) {
                // 若不存在,保存第一次监测的信息
                //第一次 number默认为1
                timeThresholdInfo = new TimeThresholdInfo(createDate, 1, locations, imgPath, modelResults);
                redisUtil.set(keyTimeThreshold, gson.toJson(timeThresholdInfo));
                logger.info("第一次监测信息键值:" + keyTimeThreshold);
                timeThresholdInfo.setFlag(false);
            } else {
                //  若存在,获取上一次监控信息
                timeThresholdInfo = gson.fromJson(String.valueOf(redisUtil.get(keyTimeThreshold)), TimeThresholdInfo.class);
                timeThresholdInfo.setNumber(timeThresholdInfo.getNumber() + 1);
                logger.info("第" + (timeThresholdInfo.getNumber() + 1) + "次监测信息键值:" + keyTimeThreshold);
                List<ModelResult> results = timeThresholdInfo.getModelResults();
                // 大于等于阈值时,比较获取最大得分的监控信息
                if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_0)) {
//                    logger.info("大于等于阈值时,本次监测覆盖时间---------"+createDate);
                    //获取本次得分的最大值
                    double maxLocal = 0.0;
                    for (ModelResult result : modelResults) {
                        maxLocal = (result.getScore() >= maxLocal) ? result.getScore() : maxLocal;
                    }
//                    logger.info("获取本次得分的最大值---------"+maxLocal);
                    //获取上次得分的最大值
                    double maxLast = 0.0;
                    for (ModelResult result : results) {
                        maxLast = (result.getScore() >= maxLast) ? result.getScore() : maxLast;
                    }
//                    logger.info("获取上次得分的最大值---------"+maxLast);
                    //若本次得分大于等于上次得分
                    if (maxLocal >= maxLast) {
//                        logger.info("本次监测覆盖时间---------"+createDate);
                        timeThresholdInfo.setLocations(locations);
                        timeThresholdInfo.setImgPath(imgPath);
                        timeThresholdInfo.setModelResults(modelResults);
                    }
                    //
                }
                // 大于零且小于等于阈值,比较获取最大得分的监控信息
                if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_1)) {
//                    logger.info("大于零且小于等于阈值时,本次监测覆盖时间---------"+createDate);
                    //获取本次得分的最大值
                    double maxLocal = 0.0;
                    for (ModelResult result : modelResults) {
                        maxLocal = (result.getScore() >= maxLocal) ? result.getScore() : maxLocal;
                    }
//                    logger.info("获取本次得分的最大值---------"+maxLocal);
                    //获取上次得分的最大值
                    double maxLast = 0.0;
                    for (ModelResult result : results) {
                        maxLast = (result.getScore() >= maxLast) ? result.getScore() : maxLast;
                    }
//                    logger.info("获取上次得分的最大值---------"+maxLast);
                    //若本次得分大于等于上次得分
                    if (maxLocal >= maxLast) {
                        timeThresholdInfo.setLocations(locations);
                        timeThresholdInfo.setImgPath(imgPath);
                        timeThresholdInfo.setModelResults(modelResults);
                    }
                }
                //小于等于阈值(包含识别没有结果),比较获取最小得分的监控信息
                if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_2)) {
//                    logger.info("小于等于阈值(包含识别没有结果)时,本次监测覆盖时间---------"+createDate);
                    // 获取本次得分的最小值
                    double minLocal = 0.0;
                    for (ModelResult result : modelResults) {
                        minLocal = (result.getScore() <= minLocal) ? result.getScore() : minLocal;
                    }
//                    logger.info("获取本次得分的最小值---------"+minLocal);
                    // 获取上次得分的最小值
                    double minLast = 0.0;
                    for (ModelResult result : results) {
                        minLast = (result.getScore() <= minLast) ? result.getScore() : minLast;
                    }
//                    logger.info("获取上次得分的最小值---------"+minLast);
                    //若本次得分小于等于上次得分
                    if (minLocal <= minLast) {
                        timeThresholdInfo.setLocations(locations);
                        timeThresholdInfo.setImgPath(imgPath);
                        timeThresholdInfo.setModelResults(modelResults);
                    }
                }
                // 本次监测时间和第一次监控时间之差是否大于等于监测时长 若大于等于返回true 不大于返回false
                boolean b = DayUtils.compareTimeMinute(timeThresholdInfo.getFirstTime(), createDate, Integer.valueOf(timeThreshold));
                if (b) {
                    logger.info("超过监测时间---------" + timeThreshold);
                    // 本次监测时间和第一次监控时间之差大于等于监测时长
                    // 获取当前任务的轮询时间
//                    PcMonitoringTask pcMonitoringTask = taskService.selectPcMonitoringTaskById(taskId);
                    String timeThresholdProportion = pcMonitoringDeviceModel.getTimeThresholdProportion(); // 监测事件次数
//                    logger.info("当前比例为---------"+time_threshold_proportion);
//                    if ((timeThresholdInfo.getNumber() + 1) >= ((Integer.valueOf(timeThreshold) * 60 / pcMonitoringTask.getTimeInterval()) * Double.valueOf(timeThresholdProportion))) {
                    if ((timeThresholdInfo.getNumber() + 1) >= Integer.parseInt(timeThresholdProportion)) {
                        // 若当前时间大于等于第一次报警后的2倍监测时长后,就不报警
                        if (DayUtils.compareTimeMinute(timeThresholdInfo.getFirstTime(), createDate, Integer.valueOf(timeThreshold) * 2)) {
                            timeThresholdInfo.setFlag(false);
                        } else {
                            timeThresholdInfo.setFlag(true);
                        }
                    } else {
                        //若不大于
                        timeThresholdInfo.setFlag(false);
                    }
                    //清除当前redis信息
                    redisUtil.del(keyTimeThreshold);
                } else {
                    logger.info("未超过监测时间---------" + timeThreshold);
                    // 本次监测时间和第一次监控时间之差小于监测时长,记录当前最好的监控信息,跳出执行
                    redisUtil.set(keyTimeThreshold, gson.toJson(timeThresholdInfo));
                    timeThresholdInfo.setFlag(false);
                }
            }
        } else {
            timeThresholdInfo = new TimeThresholdInfo();
            timeThresholdInfo.setFlag(true);
            timeThresholdInfo.setImgPath(imgPath);
            timeThresholdInfo.setLocations(locations);
            timeThresholdInfo.setModelResults(modelResults);
        }
        return timeThresholdInfo;
    }


    /**
     * 根据坐标截图保存
     *
     * @param locations             坐标地址数组
     * @param imgPath               原图地址
     * @param imei                  通道号
     * @param monitoringDeviceModel 模型
     * @return 返回框图保存的地址
     */
    public String transformImg(ArrayList<Location> locations, String imgPath, String imei, PcMonitoringDeviceModel monitoringDeviceModel, List<String> markContents) {
        String frameImgPath = imei + "/" + "frameImg" + "/" + DayUtils.getToday() + File.separator + DayUtils.getBeforeByHourTime(0);
        //新建文件夹
        FileUtil.createFile(RuoYiConfig.getFfmpefPath() + "/" + frameImgPath);
        FileUtil.createFile(RuoYiConfig.getOriginalFfmpefPath() + "/" + frameImgPath);
        String frameImgName = DayUtils.getToday() + "_" + DayUtils.getTodayTime() + "_" + UUID.randomUUID() + "_" + monitoringDeviceModel.getClassName() + "_" + monitoringDeviceModel.getClassName() + ".jpg"; //随机数图片名称
        String frameOriginalImgPath = RuoYiConfig.getOriginalFfmpefPath() + "/" + frameImgPath + "/" + frameImgName;
        frameImgPath = RuoYiConfig.getFfmpefPath() + "/" + frameImgPath + "/" + frameImgName;
        //保存原图
        try {
            FileUtil.copy(imgPath, frameOriginalImgPath);
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("原图复制失败!-----{}", e.getMessage());
        }
        Graphics g = null;
        FileOutputStream out = null;
        try {
            BufferedImage image = ImageIO.read(new File(imgPath));
            g = image.getGraphics();
            BasicStroke stokeLine = new BasicStroke(2.0f);
            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(stokeLine);
            g.setColor(Color.RED);//画笔颜色
            if (locations.size() > 0) {
                for (Location location : locations) {
                    //矩形框(原点x坐标,原点y坐标,矩形的长,矩形的宽)
                    g.drawRect(location.getX(), location.getY(), location.getWidth(), location.getHeight());
                }
            }
            // coordinates: [[{"oX":151,"oY":108},{"oX":417,"oY":130},{"oX":376,"oY":236},{"oX":196,"oY":294},{"oX":135,"oY":195}]]
            JSONArray coordinates = JSONArray.fromObject(monitoringDeviceModel.getCoordinate());
            if (coordinates.size() > 0) {
                Gson gson = new Gson();
                for (Object coordinateArrays : coordinates) {
                    JSONArray coordinateList = JSONArray.fromObject(coordinateArrays);
                    if (coordinateList.size() > 0) {
                        Polygon polygon = new Polygon();
                        for (int i = 0; i < coordinateList.size(); i++) {
                            Coordinate coordinate = gson.fromJson(coordinateList.get(i).toString(), Coordinate.class);
                            polygon.addPoint(coordinate.getoX(), coordinate.getoY());
                        }
                        //画多边形
                        g.setColor(Color.BLUE);//画笔颜色
                        g.drawPolygon(polygon);
                    }
                }
            }
            //生成水印
            int srcImgWidth = image.getWidth(null);
            int srcImgHeight = image.getHeight(null);
            // 加水印
//            g.drawImage(image, 0, 0, srcImgWidth, srcImgHeight, null);
            // Font font = new Font("Courier New", Font.PLAIN, 12);
            Font font = new Font("宋体", Font.PLAIN, srcImgWidth / 300 * 8);
            g.setColor(new Color(30, 144, 255)); // 根据图片的背景设置水印颜色
            g.setFont(font);
            for (int i = 0; i < markContents.size(); i++) {
                int x = 10;
                int y = srcImgHeight - srcImgHeight / 5 + srcImgWidth / 300 * 8 * i + 10;
                g.drawString(markContents.get(i), x, y);
            }
            out = new FileOutputStream(frameImgPath);//输出图片的地址
            ImageIO.write(image, "jpeg", out);
            out.flush();
        } catch (IOException e) {
            logger.error("根据坐标截图保存失败" + e);
            return null;
        } finally {
            g.dispose();
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                logger.error("输出流关闭失败");
            }
        }
        return frameImgPath;
    }

    /**
     * 保存告警信息
     *
     * @param createDate            创建时间
     * @param modelResults          模型调用返回结果
     * @param frameImgPath          框图保存地址
     * @param pcMonitoringDevice    通道信息
     * @param monitoringDeviceModel 模型信息
     */
    public void saveMonitoringDeviceResult(String createDate, List<ModelResult> modelResults, String frameImgPath, PcMonitoringDevice pcMonitoringDevice, PcMonitoringDeviceModel monitoringDeviceModel) {
        PcMonitoringDeviceResult monitoringDeviceResult = new PcMonitoringDeviceResult();
        monitoringDeviceResult.setDelFlag(URLConstant.DELFLAG_0); // 未删除标志
        monitoringDeviceResult.setCreateId(pcMonitoringDevice.getCreateId());  // 创建人
        monitoringDeviceResult.setCreateDate(createDate); // 创建时间
        monitoringDeviceResult.setDeptId(pcMonitoringDevice.getDeptId()); // 创建部门ID
        monitoringDeviceResult.setMonitoringDeviceId(monitoringDeviceModel.getMonitoringDeviceId()); // 监控通道ID
        monitoringDeviceResult.setAiModelId(monitoringDeviceModel.getAiModelId()); // ai模型ID
        monitoringDeviceResult.setSimilarity(monitoringDeviceModel.getSimilarity()); // ai模型相似度
        monitoringDeviceResult.setImgUrl(Constants.RESOURCE_PREFIX + frameImgPath.replace(RuoYiConfig.getProfile(), "")); // 截取并拼接保存图片URL访问地址
        monitoringDeviceResult.setResult(modelResults.toString()); //接口调用识别结果详情
        monitoringDeviceResult.setAlarmEvent(0L);//设置事件类型
        monitoringDeviceResult.setSynStatus(5);
        int i = iPcMonitoringDeviceResultService.insertPcMonitoringDeviceResult(monitoringDeviceResult);
        if (i > 0) {
            PcDataReportConfig pcDataReportConfig = pcDataReportConfigService.selectPcDataReportConfigById(1L);
            //数据上报顶层平台
            if (iPcMonitoringDeviceResultService.synDataReport(monitoringDeviceResult.getId(), "add", pcDataReportConfig)) {
                logger.info("告警信息(id->{})信息上报成功!", monitoringDeviceResult.getId());
            } else {
                logger.info("告警信息(id->{})信息上报失败!", monitoringDeviceResult.getId());
            }
        }
        //判断当前通道、当前模型是否允许告警推送
        boolean isTimeInterval = isTimeInterval(createDate, monitoringDeviceModel);
        if (isTimeInterval) {
            pool.execute(() -> {
                //通道告警通知 短信、钉钉、企业微信、邮箱、第三方接口
                pcAlarmConfigService.sendMonitoringMsg(frameImgPath, monitoringDeviceResult);
            });
        }
        publishAlarmInformation(monitoringDeviceResult, pcMonitoringDevice.getDeptName(), monitoringDeviceModel.getAiModelName(), pcMonitoringDevice.getName());
    }

    @SuppressWarnings("all")
    public void publishAlarmInformation(PcMonitoringDeviceResult monitoringDeviceResult, String deptName, String aiModelName, String monitoringDeviceName) {
        String screen_url = configService.selectConfigByKey(URLConstant.SCREEN_URL);
        if (null == screen_url || screen_url.trim().length() < 1 || screen_url.equals("screen_url"))
            return;
        //判断连接状态用户是否为null
        ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentHashMap<String, WebSocketServer>>> webSocketSet = webSocketServer.webSocketSet;
        if (CollectionUtil.isEmpty(webSocketSet))
            return;

        //获取该部门及其父部门id
        List<Long> parentDeptIdList = sysDeptService.getParentDeptIdList(monitoringDeviceResult.getDeptId());

        ConcurrentHashMap<String, ConcurrentHashMap<String, WebSocketServer>> apiScreen = webSocketSet.get("screenAlarm");
        if (CollectionUtil.isNotEmpty(apiScreen)) {
            //判断推送视频流
            //判断当前部门下的用户及其父部门
            Map<String, ConcurrentHashMap<String, WebSocketServer>> sendUserMap = apiScreen.entrySet().stream().filter(l -> parentDeptIdList.contains(userService.selectUserById(Long.valueOf(l.getKey())).getDeptId())).collect(Collectors.toMap(l -> l.getKey(), k -> k.getValue(), (oldValue, newValue) -> oldValue));
            sendUserMap.entrySet().stream().forEach(entry -> {
                try {
                    //创建发布实体类
                    String is_or_not_important_alarm = configService.selectConfigByKey(URLConstant.IS_OR_NOT_IMPORTANT_ALARM);
                    List<String> isOrNotImportantAlarm = StringUtils.isNotBlank(is_or_not_important_alarm) ? Arrays.asList(is_or_not_important_alarm.split(",")) : null;
                    Map<String, Object> hashMap = new HashMap<>();
                    hashMap.put("id", monitoringDeviceResult.getId());
                    hashMap.put("createDate", monitoringDeviceResult.getCreateDate());
                    hashMap.put("deptName", deptName);
                    hashMap.put("aiModelName", monitoringDeviceResult.getAiModelName());
                    hashMap.put("alarmEvent", monitoringDeviceResult.getAlarmEvent());
                    hashMap.put("imgUrl", screen_url + monitoringDeviceResult.getImgUrl());
                    hashMap.put("monitoringDeviceName", monitoringDeviceResult.getMonitoringDeviceName());
                    hashMap.put("importantAlarm", !Optional.ofNullable(isOrNotImportantAlarm).isPresent() ? false : isOrNotImportantAlarm.contains(String.valueOf(monitoringDeviceResult.getAiModelId())));

                    entry.getValue().entrySet().stream().forEach(entry_1 -> publishAlarmInformation(entry_1.getValue(), gson.toJson(hashMap), "apiScreen", "publishAlarmInformation"));
                } catch (Exception e) {
                    logger.error("推送告警画面失败----->告警信息id{}-------->用户id{}", monitoringDeviceResult.getId(), entry.getKey());
                    logger.error("推送告警画面失败" + e);
                }
            });
        }
    }

    @SuppressWarnings("all")
    public void publishAlarmInformation(WebSocketServer webSocketServer, String hashMap, String
            moduleName, String methodName) {
        try {
            HashMap<String, Object> map = new HashMap<>();
            map.put("code", 200);
            map.put("errMessage", "");
            map.put("data", hashMap);
            Map<String, Object> data = new HashMap<>();
            data.put("moduleName", moduleName);
            data.put("methodName", methodName);
            data.put("params", map);
            webSocketServer.sendMessage(gson.toJson(data));
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("Error sending json map!");
        }

    }

    /**
     * 判断当前通道、当前模型是否允许告警推送
     *
     * @param createDate
     * @param monitoringDeviceModel
     * @return
     */
    private boolean isTimeInterval(String createDate, PcMonitoringDeviceModel monitoringDeviceModel) {
        boolean isTimeInterval = false;
        if (monitoringDeviceModel.getAlarmStatus().equals(URLConstant.PC_MODEL_ALARM_ON)) {
            // 若允许推送,判断告警推送间隔时间是否等于0
            String timeInterval = monitoringDeviceModel.getTimeInterval();
            if (timeInterval.equals("0")) {
                isTimeInterval = true;
            } else {
                //若不等于0,查询当前通道当前模型上一次告警推送时间(从redis中取)
                String key_time_interval = monitoringDeviceModel.getMonitoringDeviceId() + "_" + monitoringDeviceModel.getAiModelId() + "_" + monitoringDeviceModel.getClassName() + "_" + monitoringDeviceModel.getLabel() + "_time_interval";
                if (!redisUtil.hasKey(key_time_interval)) {
                    // 若不存在,立即推送,并记录当前创建时间到redis
                    logger.info("第一次告警推送时间:" + key_time_interval);
                    redisUtil.set(key_time_interval, createDate);
                    isTimeInterval = true;
                } else {
                    // 若存在,比较上一次告警推送时间和创建时间
                    //获取上一次告警推送时间
                    String redisTime = String.valueOf(redisUtil.get(key_time_interval));
                    // 创建时间和上一次告警推送时间之差大于等于告警间隔时间 若大于等于返回true 不大于返回false
                    boolean b = DayUtils.compareTimeMinute(redisTime, createDate, Integer.valueOf(timeInterval));
                    if (b) {
                        // 若创建时间和上一次告警时间之差大于等于告警间隔时间,立即推送 true
                        redisUtil.set(key_time_interval, createDate);
                        isTimeInterval = true;
                    } else {
                        // 若创建时间和上一次告警时间之差小于告警间隔时间,跳过推送 false
                        isTimeInterval = false;
                    }
                }
            }
        }
        return isTimeInterval;
    }

}

CheckResultAlarmRule
package com.ruoyi.pc.domain;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.pc.task.util.Location;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 监控通道对象 pc_monitoring_device
 *
 * @author sc
 * @date 2021-03-09
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class CheckResultAlarmRule extends BaseEntity {
    private boolean alarm;

    private ArrayList<Location> locations;

    private List<ModelResult> modelResults;
}

public class PcVideoServiceImpl implements IPcVideoService
package com.ruoyi.pc.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.pc.config.redis.RedisUtil;
import com.ruoyi.pc.domain.*;
import com.ruoyi.pc.mapper.PcMonitoringDeviceMapper;
import com.ruoyi.pc.mapper.PcMonitoringTaskDeviceMapper;
import com.ruoyi.pc.mapper.PcMonitoringTaskMapper;
import com.ruoyi.pc.service.*;
import com.ruoyi.pc.task.VideoStreamTask;
import com.ruoyi.pc.util.*;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.system.service.ISysConfigService;
import lombok.extern.log4j.Log4j2;
import net.sf.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.CountDownLatch;

@Service
@Log4j2
public class PcVideoServiceImpl implements IPcVideoService {

    public static Logger logger = LoggerFactory.getLogger(VideoStreamTask.class);

    Gson gson = new Gson();

    @Value("${server.port}")
    private int serverPort;

    @Resource
    ThreadPoolTaskExecutor pool;

    @Autowired
    IPcMonitoringTaskService pcMonitoringTaskService;
    @Autowired
    ISysConfigService configService;
    @Autowired
    private IPcMonitoringDeviceService pcMonitoringDeviceService;
    @Autowired
    FasterRcnnHttpService httpService;

    @Autowired
    IPcMonitoringDeviceModelService iPcMonitoringDeviceModelService;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    IPcMonitoringTaskDeviceService iPcMonitoringTaskDeviceService;

    @Autowired
    private ISysJobService sysJobService;

    @Resource
    private PcMonitoringTaskDeviceMapper pcMonitoringTaskDeviceMapper;

    @Resource
    private PcMonitoringDeviceMapper pcMonitoringDeviceMapper;

    @Resource
    private PcMonitoringTaskMapper pcMonitoringTaskMapper;

    @Resource
    AiTaskService aiTaskService;

    @Resource
    TaskService taskService;


    @Override
    public synchronized void startVideoStreamByTaskId(Long taskId, boolean initialStatus, boolean isOrNotUpdateVideoStreamTaskStatus) {
        //获取当前监控任务
        PcMonitoringTask pcMonitoringTask = pcMonitoringTaskMapper.selectPcMonitorDeviceTaskByTaskId(taskId);
        //查询监控任务下开启的监控通道
        List<PcMonitoringDevice> monitoringDeviceIdList = pcMonitoringTaskDeviceMapper.selectPcMonitorDeviceByTaskId(taskId);
        if (null != pcMonitoringTask && null != monitoringDeviceIdList && monitoringDeviceIdList.size() > 0) {
            CountDownLatch countDownLatch = new CountDownLatch(monitoringDeviceIdList.size());
            // 更新监控任务状态
            PcMonitoringTask pcMonitoringTaskStatus = new PcMonitoringTask();
            pcMonitoringTaskStatus.setId(taskId);
            pcMonitoringTaskStatus.setStatus(URLConstant.PC_TASK_STATUS_CARRY_OUT);
            pcMonitoringTaskMapper.updatePcMonitoringTask(pcMonitoringTaskStatus);
            redisUtil.hset("taskThreadPoolExecutor", taskId + "", 0, 300);
            for (PcMonitoringDevice pcMonitoringDevice : monitoringDeviceIdList) {
                taskService.taskAsync(pcMonitoringTask, taskId, pcMonitoringDevice, countDownLatch, initialStatus, isOrNotUpdateVideoStreamTaskStatus);
            }
        }
    }

    @Override
    public synchronized void startVideoStreamByMonitorDeviceId(Long taskId, Long monitorDeviceId, boolean initialStatus, boolean isOrNotUpdateVideoStreamTaskStatus) {
        //获取当前监控任务
        PcMonitoringTask pcMonitoringTask = pcMonitoringTaskMapper.selectPcMonitorDeviceTaskByTaskId(taskId);
        PcMonitoringDevice pcMonitoringDevice = pcMonitoringDeviceMapper.selectPcMonitorDevice(monitorDeviceId);
        if (null != pcMonitoringTask && null != pcMonitoringDevice) {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            // 更新监控任务状态
            PcMonitoringTask pcMonitoringTaskStatus = new PcMonitoringTask();
            pcMonitoringTaskStatus.setId(taskId);
            pcMonitoringTaskStatus.setStatus(URLConstant.PC_TASK_STATUS_CARRY_OUT);
            pcMonitoringTaskMapper.updatePcMonitoringTask(pcMonitoringTaskStatus);
            redisUtil.hset("taskThreadPoolExecutor", taskId + "", 0, 300);
            taskService.taskAsync(pcMonitoringTask, taskId, pcMonitoringDevice, countDownLatch, initialStatus, isOrNotUpdateVideoStreamTaskStatus);
        }
    }

    @Override
    public synchronized void stopVideoStreamByTaskId(Long taskId, boolean initialStatus, boolean isOrNotUpdateVideoStreamTaskStatus) {
        //获取当前监控任务下的监控通道
        List<PcMonitoringDevice> monitoringDeviceIdList = pcMonitoringTaskDeviceMapper.selectPcMonitorDeviceByTaskId(taskId);
        CountDownLatch countDownLatch = new CountDownLatch(monitoringDeviceIdList.size());
        // 更新监控任务状态
        PcMonitoringTask pcMonitoringTaskStatus = new PcMonitoringTask();
        pcMonitoringTaskStatus.setId(taskId);
        pcMonitoringTaskStatus.setStatus(URLConstant.PC_TASK_STATUS_CARRY_OUT);
        pcMonitoringTaskMapper.updatePcMonitoringTask(pcMonitoringTaskStatus);
        redisUtil.hset("taskThreadPoolExecutor", taskId + "", 1, 300);
        for (PcMonitoringDevice pcMonitoringDevice : monitoringDeviceIdList) {
            taskService.taskAsync(null, taskId, pcMonitoringDevice, countDownLatch, initialStatus, isOrNotUpdateVideoStreamTaskStatus);
        }
    }

    @Override
    public synchronized void stopVideoStreamByMonitorDeviceId(Long taskId, Long monitorDeviceId, boolean initialStatus, boolean isOrNotUpdateVideoStreamTaskStatus) {
        PcMonitoringDevice pcMonitoringDevice = pcMonitoringDeviceMapper.selectPcMonitorDevice(monitorDeviceId);
        if (pcMonitoringDevice != null) {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            // 更新监控任务状态
            PcMonitoringTask pcMonitoringTaskStatus = new PcMonitoringTask();
            pcMonitoringTaskStatus.setId(taskId);
            pcMonitoringTaskStatus.setStatus(URLConstant.PC_TASK_STATUS_CARRY_OUT);
            pcMonitoringTaskMapper.updatePcMonitoringTask(pcMonitoringTaskStatus);
            redisUtil.hset("taskThreadPoolExecutor", taskId + "", 1, 300);
            taskService.taskAsync(null, taskId, pcMonitoringDevice, countDownLatch, initialStatus, isOrNotUpdateVideoStreamTaskStatus);
        }
    }

    /**
     * 处理视频流回调返回结果
     *
     * @param videoResult videoResult
     */
    @Override
    public void videoResult(VideoResult videoResult) {
        pool.execute(() -> {
            try {
                if (videoResult.getMessage() == null || Objects.equals(videoResult.getMessage(), "")) {
                    String videoId = videoResult.getVideoId();//视频流获取监控通道 通道唯一编码
                    if (null == videoId || "".equals(videoId) || !redisUtil.hasKey("task_id_" + videoId)) {
                        logger.error("当前结果taskId未保存到redis里,请查询redis确认task_id_" + videoId + "是否存在");
                        return;
                    }
                    String className = videoResult.getModelName();
                    if (null == className || "".equals(className)) {
                        logger.error("当前结果返回的模型标识为空");
                        return;
                    }
                    Long taskId = Long.valueOf(String.valueOf(redisUtil.get("task_id_" + videoId)));//从redis中获取当前通道的任务Id
                    if (null == taskId) {
                        logger.error("从redis中获取当前通道的任务Id为空");
                        return;
                    }
                    //查询唯一启用通道
                    PcMonitoringDevice pcMonitoringDeviceQuery = new PcMonitoringDevice();
                    pcMonitoringDeviceQuery.setUniqueCode(videoId);
                    pcMonitoringDeviceQuery.setAppStatus(URLConstant.STATUS_DEVICE_ACTOVATION);
                    List<PcMonitoringDevice> pcMonitoringDevices = pcMonitoringDeviceService.selectPcMonitoringDeviceList(pcMonitoringDeviceQuery);
                    if (null == pcMonitoringDevices || pcMonitoringDevices.size() == 0) {
                        logger.error("当前已启用的监控通道不存在");
                        return;
                    }
                    PcMonitoringDevice pcMonitoringDevice = pcMonitoringDevices.get(0);
                    //3、根据通道ID,获取关联的模型列表  (模型未删除、已启用、且模型地址不为空、通道相似度不为空的模型列表)
                    List<PcMonitoringDeviceModel> pcMonitoringDeviceModels = iPcMonitoringDeviceModelService.selectPcAiModelListByMonitoringDeviceId(pcMonitoringDevice.getId());
                    if (null == pcMonitoringDeviceModels || pcMonitoringDeviceModels.size() == 0) {
                        logger.error("当前通道关联的模型列表不存在");
                        return;
                    }
                    //4、根据通道信息,截取图片,返回截图地址
                    String imgPath = videoResult.getImagePath();
                    //4.1、判断截图地址是否为空
                    if (null == imgPath) {
                        logger.error("通道截取图片失败,通道号为---------------" + pcMonitoringDevices.get(0).getImei());
                        return; // 图片地址为空,跳过当前通道,继续下个通道
                    }
                    //4.2、判断截图是否成功
                    File file = new File(imgPath);
                    if (file.exists()) {
                        String createDate = DayUtils.getNow();
                        //判断当前是否存在识别类别是2的模型
                        List<ModelResult> result = videoResult.getResult();
                        // 7、遍历当前通道关联模型列表
                        String key = "";
                        for (PcMonitoringDeviceModel pcMonitoringDeviceModel : pcMonitoringDeviceModels) {
                            boolean isOrNotSaveFlag = false;
                            if (pcMonitoringDeviceModel.getEnableDeduplication() != null && StringUtils.isNotBlank(pcMonitoringDeviceModel.getEnableDeduplication())
                                    && pcMonitoringDeviceModel.getDeduplicationMode() != null && StringUtils.isNotBlank(pcMonitoringDeviceModel.getDeduplicationMode())
                                    && pcMonitoringDeviceModel.getDeduplicationDuration() != null && StringUtils.isNotBlank(pcMonitoringDeviceModel.getDeduplicationDuration())) {
                                if (Objects.equals(pcMonitoringDeviceModel.getEnableDeduplication(), "0")) {
                                    key = "quantity_detection" + taskId + pcMonitoringDeviceModel.getMonitoringDeviceId() + pcMonitoringDeviceModel.getAiModelId() + pcMonitoringDeviceModel.getId();
                                } else if (Objects.equals(pcMonitoringDeviceModel.getEnableDeduplication(), "1")) {
                                    key = "coordinate_detection" + taskId + pcMonitoringDeviceModel.getMonitoringDeviceId() + pcMonitoringDeviceModel.getAiModelId() + pcMonitoringDeviceModel.getId();
                                } else if (Objects.equals(pcMonitoringDeviceModel.getEnableDeduplication(), "2")) {
                                    key = "deduplication_identification" + taskId + pcMonitoringDeviceModel.getMonitoringDeviceId() + pcMonitoringDeviceModel.getAiModelId() + pcMonitoringDeviceModel.getId();
                                }
                            }
                            if (URLConstant.MODEL_TYPE_2.equals(pcMonitoringDeviceModel.getType())) {
                                String pc_http_url = configService.selectConfigByKey(URLConstant.PC_HTTP_URL);
                                if (null != pc_http_url && pc_http_url.trim().length() != 0 && !pc_http_url.equals("false")) {
                                    Map<String, Object> stringObjectMap = new HashMap<>();
                                    stringObjectMap.put("taskId", taskId);
                                    stringObjectMap.put("pcMonitoringDeviceModelId", pcMonitoringDeviceModel.getId());
                                    stringObjectMap.put("monitoringDeviceId", pcMonitoringDevice.getId());
                                    stringObjectMap.put("createDate", createDate);
                                    stringObjectMap.put("frontAlgoResult", result);
                                    getModelResultHttp(imgPath, pc_http_url, pcMonitoringDeviceModel.getClassName(), pcMonitoringDeviceModel.getLabel(), stringObjectMap);
                                    continue;
                                }
                            }
                            // 8.2、判断识别结果是否为空
                            if (null != result && result.size() > 0) {
                                // 8.3 当模型比较类型是大于等于相似度阈值时
                                if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_0)) {
                                    //8.3.1、筛选大于等于相似度阈值的识别结果
                                    List<ModelResult> modelResults = new ArrayList<ModelResult>(); //筛选后识别结果
                                    for (int i = 0; i < result.size(); i++) {
                                        // 识别结果格式化成ModelResult
                                        ModelResult modelResult = result.get(i);
                                        // 获取结果category等于当前模型label,且大于等于相似度阈值
                                        if (modelResult.getCategory().equals(pcMonitoringDeviceModel.getLabel())
                                                && Double.valueOf(modelResult.getScore()) >= Double.parseDouble(pcMonitoringDeviceModel.getSimilarity())) {
                                            isOrNotSaveFlag = true;
                                            modelResults.add(modelResult);
                                        }
                                    }

                                    List<ModelResult> personFrontAlgo = getPersonFrontAlgo(createDate, taskId, imgPath, pcMonitoringDeviceModel, modelResults);
                                    // 8.3.2、筛选后识别结果数 大于等于 告警目标数阈值
                                    if (null != personFrontAlgo && personFrontAlgo.size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber())) {
                                        // 8.3.3、发布到redis通道
                                        publishAiMessage(taskId, personFrontAlgo, createDate, imgPath, pcMonitoringDevices.get(0), pcMonitoringDeviceModel);
                                        logger.info("任务ID--------" + taskId + ",发布时间--------" + createDate + ",通道ID--------" + pcMonitoringDeviceModel.getMonitoringDeviceId() + ",配置模型ID--------" + pcMonitoringDeviceModel.getId() + ",模型标识--------" + pcMonitoringDeviceModel.getClassName() + ",模型标签------" + pcMonitoringDeviceModel.getLabel() + ",模型识别结果--------" + gson.toJson(modelResults));
                                    }
                                }
                                // 8.4 当模型比较类型是大于零且小于等于相似度阈值时
                                if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_1)) {
                                    //8.4.1、筛选大于零且小于等于相似度阈值的识别结果
                                    List<ModelResult> modelResults = new ArrayList<ModelResult>(); //筛选后识别结果
                                    for (int i = 0; i < result.size(); i++) {
                                        // 识别结果格式化成ModelResult
                                        ModelResult modelResult = result.get(i);
                                        // 获取结果category等于当前模型label,且大于等于相似度阈值
                                        if (modelResult.getCategory().equals(pcMonitoringDeviceModel.getLabel())
                                                && Double.valueOf(modelResult.getScore()) > 0
                                                && Double.valueOf(modelResult.getScore()) <= Double.parseDouble(pcMonitoringDeviceModel.getSimilarity())) {
                                            isOrNotSaveFlag = true;
                                            modelResults.add(modelResult);
                                        }
                                    }
                                    List<ModelResult> personFrontAlgo = getPersonFrontAlgo(createDate, taskId, imgPath, pcMonitoringDeviceModel, modelResults);
                                    // 8.4.2、筛选后识别结果数 大于等于 告警目标数阈值
                                    if (null != personFrontAlgo && personFrontAlgo.size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber())) {
                                        // 8.4.3、发布到redis通道
                                        publishAiMessage(taskId, personFrontAlgo, createDate, imgPath, pcMonitoringDevices.get(0), pcMonitoringDeviceModel);
                                        logger.info("任务ID--------" + taskId + ",发布时间--------" + createDate + ",通道ID--------" + pcMonitoringDeviceModel.getMonitoringDeviceId() + ",配置模型ID--------" + pcMonitoringDeviceModel.getId() + ",模型标识--------" + pcMonitoringDeviceModel.getClassName() + ",模型标签------" + pcMonitoringDeviceModel.getLabel() + ",模型识别结果--------" + gson.toJson(modelResults));
                                    }
                                }
                            } else {
                                if (StringUtils.isNotBlank(key) && redisUtil.hasKey(key)) {
                                    if (key.startsWith("quantity_detection")) {
                                        Map<Object, Object> hmget = redisUtil.hmget(key);
                                        long expire = redisUtil.getExpire(key);
                                        if (hmget.size() == 1) {
                                            redisUtil.hset(key, "2", DayUtils.getNow() + "_" + 0, expire);
                                        } else if (hmget.size() == 2) {
                                            redisUtil.hset(key, "3", DayUtils.getNow() + "_" + 0, expire);
                                        } else if (hmget.size() == 3) {
                                            Object second = redisUtil.hget(key, "2");
                                            Object three = redisUtil.hget(key, "3");
                                            redisUtil.del(key);
                                            redisUtil.hset(key, "1", second, expire);
                                            redisUtil.hset(key, "2", three, expire);
                                            redisUtil.hset(key, "3", DayUtils.getNow() + "_" + 0, expire);
                                        }
                                    } else {
                                        logger.error("-------------------------------------------删除了", key);
                                        redisUtil.del(key);
                                    }
                                }
                            }
                            // 8.3、若模型设置保留截图,保存原图
                            if (pcMonitoringDeviceModel.getIsSave().equals(URLConstant.TYPE_DEVICE_ON) && isOrNotSaveFlag) {
                                pool.execute(() -> {
                                    try {
                                        //根据模型保存图片
                                        //保存实体路径为 ffmpeg图片保存地址+通道号+当天日期
                                        String baseImgPath = RuoYiConfig.getModelImgPath() + "/" + pcMonitoringDevice.getImei() + "/" + pcMonitoringDeviceModel.getAiModelId() + "/" + DayUtils.getToday();
                                        //新建文件夹
                                        FileUtil.createFile(baseImgPath);
                                        baseImgPath = baseImgPath + "/" + DayUtils.getToday() + "_" + DayUtils.getTodayTime() + "_" + UUID.randomUUID() + ".jpg";
                                        FileUtil.copy(imgPath, baseImgPath);
                                    } catch (IOException e) {
                                        logger.error("保存原图失败" + e);
                                    }
                                });
                            }

                        }
                    } else {
                        logger.error("{} 文件不存在++++++++++++++++++++", imgPath);
                    }
                } else {
                    //结果失败
                    logger.error("当前处理是视频流处理出现错误时返回的信息,请根据相关信息检查出现错误的原因:" + "任务ID--------" + redisUtil.get("task_id_" + videoResult.getVideoId()) + ",code--------" + videoResult.getCode() + ",视频流ID--------" + videoResult.getVideoId() + ",失败信息------" + videoResult.getMessage());
                }
            } catch (Exception e) {
                logger.error("视频流回调处理结果报错" + e);
            }
        });
    }

    public void getOffDutyDetection(String createDate, Long taskId, String imgPath, PcMonitoringDeviceModel pcMonitoringDeviceModel, List<ModelResult> result, PcMonitoringDevice pcMonitoringDevice) {
        if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_2)) {
            //8.5.1、筛选大于零且小于等于相似度阈值的识别结果
            List<ModelResult> modelResults = new ArrayList<ModelResult>(); //筛选后识别结果
            if (result.size() > 0) {
                for (int i = 0; i < result.size(); i++) {
                    // 识别结果格式化成ModelResult
                    ModelResult modelResult = result.get(i);
                    // 获取结果category等于当前模型label
                    if (modelResult.getCategory().equals(pcMonitoringDeviceModel.getLabel())) {
                        modelResults.add(modelResult);
                    }
                }
            }
            // 8.5.2、筛选后识别结果数等于0
            if (modelResults.size() == 0) {
                ModelResult modelResult = new ModelResult();
                modelResult.setCategory(pcMonitoringDeviceModel.getLabel());
                modelResult.setBbox(new ArrayList<>());
                modelResult.setScore(0);
                modelResults.add(modelResult);
            }
            // 8.5.3、发布到redis通道
            publishAiMessage(taskId, modelResults, createDate, imgPath, pcMonitoringDevice, pcMonitoringDeviceModel);
            logger.info("小于等于阈值(包含识别没有结果)--------任务ID--------" + taskId + ",发布时间--------" + createDate + ",通道ID--------" + pcMonitoringDeviceModel.getMonitoringDeviceId() + ",配置模型ID--------" + pcMonitoringDeviceModel.getId() + ",模型标识--------" + pcMonitoringDeviceModel.getClassName() + ",模型标签------" + pcMonitoringDeviceModel.getLabel() + ",模型识别结果--------" + gson.toJson(modelResults));
        }
    }

    /**
     * 处理视频流前置算法回调返回结果
     *
     * @param videoResult
     */
    @Override
    public void videoFrontAlgoResult(VideoResult videoResult) {
        pool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Map<String, Object> inferParameterMap = videoResult.getInferParameter();
                    Object pcMonitoringDeviceModelId = inferParameterMap.get("pcMonitoringDeviceModelId");
                    if (null == pcMonitoringDeviceModelId || "".equals(pcMonitoringDeviceModelId)) {
                        logger.error("前置算法缺少监控通道关联模型Id【***************************************】");
                        return;
                    }
                    PcMonitoringDeviceModel pcMonitoringDeviceModel = iPcMonitoringDeviceModelService.selectPcMonitoringDeviceModelId(Long.valueOf(String.valueOf(pcMonitoringDeviceModelId)));
                    if (null == pcMonitoringDeviceModel) {
                        logger.error("前置算法数据库缺少监控通道关联模型【***************************************】");
                        return;
                    }

                    Object pcMonitoringDeviceId = inferParameterMap.get("monitoringDeviceId");
                    if (null == pcMonitoringDeviceId || "".equals(pcMonitoringDeviceId)) {
                        logger.error("前置算法缺少监控通道Id【***************************************】");
                        return;
                    }
                    PcMonitoringDevice pcMonitoringDeviceQuery = new PcMonitoringDevice();
                    pcMonitoringDeviceQuery.setId(Long.valueOf(String.valueOf(pcMonitoringDeviceId)));
                    List<PcMonitoringDevice> pcMonitoringDevices = pcMonitoringDeviceService.selectPcMonitoringDeviceList(pcMonitoringDeviceQuery);
                    if (null == pcMonitoringDevices || pcMonitoringDevices.size() == 0) {
                        logger.error("前置算法数据库缺少监控通道【***************************************】");
                        return;
                    }

                    String imagePath = videoResult.getImagePath();
                    if (null == imagePath || "".equals(imagePath)) {
                        logger.error("前置算法缺少图片路径【***************************************】");
                        return;
                    }
                    File file = new File(imagePath);
                    if (!file.exists()) {
                        logger.error("前置算法缺少图片文件【***************************************】");
                        return;
                    }
                    Object taskId = inferParameterMap.get("taskId");
                    if (null == taskId || "".equals(taskId)) {
                        logger.error("前置算法缺少任务Id【***************************************】");
                        return;
                    }
                    Object createDate = inferParameterMap.get("createDate");
                    if (null == createDate || "".equals(createDate)) {
                        logger.error("前置算法缺少创建时间【***************************************】");
                        return;
                    }
                    if (pcMonitoringDeviceModel.getType().equals(URLConstant.MODEL_TYPE_2)) {
                        getOffDutyDetection(String.valueOf(createDate), Long.parseLong(String.valueOf(taskId)), imagePath, pcMonitoringDeviceModel, videoResult.getResult(), pcMonitoringDevices.get(0));
                        return;
                    }
                    Object resultOld = inferParameterMap.get("frontAlgoResult");
                    if (null == resultOld || "".equals(resultOld)) {
                        logger.error("前置算法缺少算法识别结果【***************************************】");
                        return;
                    }
                    //人体前置算法识别结果
                    List<ModelResult> result = videoResult.getResult();
                    if (null == result || result.size() == 0) {
                        logger.error("前置算法缺少算法识别结果【***************************************】");
                        return;
                    }
                    //算法识别结果
                    JSONArray resultOldJSONArray = JSONArray.fromObject(resultOld);

                    //如果是非需要两次结果的交集的   就是按照第二次结果与ROI进行比较 则过滤结果是第二次的结果
                    boolean needCheckForFirstResult = (boolean) inferParameterMap.get("needCheckForFirstResult");
                    List<ModelResult> filterResult;
                    if (!needCheckForFirstResult){
                        //不需要与第一次结果进行比较,则直接进行第二次结果的过滤
                        String personBodyPreModelIds = configService.selectConfigByKey(URLConstant.PERSON_BODY_PRE_MODEL_IDS);
                        String[] split = personBodyPreModelIds.split(",");
                        String resVal = null;
                        for (String s : split) {
                            String[] split1 = new String[0];
                            if (s.contains("&")){
                                split1 = s.split("&");
                                s = split1[0];
                            }
                            if (pcMonitoringDeviceModel.getAiModelId() == Long.parseLong(s)){
                                resVal = split1.length > 1 ? split1[1] : pcMonitoringDeviceModel.getSimilarity();
                            }
                        }
                        pcMonitoringDeviceModel.setSimilarity(resVal == null ? pcMonitoringDeviceModel.getSimilarity() : resVal);

                        CheckResultAlarmRule checkResultAlarmRule = aiTaskService.checkResultRule(pcMonitoringDeviceModel, result);
                        filterResult = checkResultAlarmRule.getModelResults();
                    }else {
                        filterResult = getFilterResult(result, resultOldJSONArray);
                    }


                    if (null != filterResult && filterResult.size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber())) {
                        // 8.3.3、发布到redis通道
                        publishAiMessage(Long.valueOf(String.valueOf(taskId)), filterResult, String.valueOf(createDate), imagePath, pcMonitoringDevices.get(0), pcMonitoringDeviceModel);
                        logger.info("任务ID--------" + taskId + ",发布时间--------" + createDate + ",通道ID--------" + pcMonitoringDeviceModel.getMonitoringDeviceId() + ",配置模型ID--------" + pcMonitoringDeviceModel.getId() + ",模型标识--------" + pcMonitoringDeviceModel.getClassName() + ",模型标签------" + pcMonitoringDeviceModel.getLabel() + ",模型识别结果--------" + gson.toJson(filterResult));
                    }

                } catch (Exception e) {
                    logger.error("前置算法处理结果报错" + e);
                }
            }
        });
    }

    /**
     * 自启动视频流
     *
     * @return
     */
    public void selfStartVideoStream() {
        logger.info("======================自动开启视频流========================");
        try {
            PcMonitoringTask pcMonitoringTask = new PcMonitoringTask();
            List<PcMonitoringTask> pcMonitoringTaskList = pcMonitoringTaskService.selectPcMonitoringTaskList(pcMonitoringTask);
            for (PcMonitoringTask pcMonitoringTask1 : pcMonitoringTaskList) {
                if (pcMonitoringTask1.getStatus().equals(URLConstant.MONITORING_TASK_STATUS_ENABLE)) {
                    //判断当前任务是视频分析还是抽帧检测
                    if (pcMonitoringTask1.getRecognitionMode().equals(URLConstant.RECOGNITION_MODE_VIDEO_ANALYSIS_PUSH_FLOW) || pcMonitoringTask1.getRecognitionMode().equals(URLConstant.RECOGNITION_MODE_VIDEO_ANALYSIS_NOT_PUSH_FLOW)) {
                        //是否在任务执行期间
                        boolean rNotTriggerStartVideoStream = sysJobService.is0rNotTriggerStartVideoStream(pcMonitoringTask1);
                        if (rNotTriggerStartVideoStream) {
                            startVideoStreamByTaskId(pcMonitoringTask1.getId(), true, false);
                            Thread.sleep(5000);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("视频流自启动失败", e.getMessage());
        }
    }

    /**
     * 获取当前设备的主机号和端口号
     *
     * @return
     */
    public String getUrl() {
        InetAddress address = null;
        try {
            address = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return "http://" + address.getHostAddress() + ":" + this.serverPort;
    }

    /**
     * 发布消息到redis中
     *
     * @param modelResults
     * @param createDate
     * @param imgPath
     * @param pcMonitoringDevice
     * @param pcMonitoringDeviceModel
     */
    @Async
    public void publishAiMessage(Long taskId, List<ModelResult> modelResults, String createDate, String imgPath, PcMonitoringDevice pcMonitoringDevice, PcMonitoringDeviceModel pcMonitoringDeviceModel) {
        AiMessage aiMessage = new AiMessage();
        aiMessage.setTaskId(taskId);
        aiMessage.setCreateDate(createDate);
        aiMessage.setImgPath(imgPath);
        aiMessage.setPcMonitoringDevice(pcMonitoringDevice);
        aiMessage.setPcMonitoringDeviceModel(pcMonitoringDeviceModel);
        aiMessage.setModelResults(modelResults);
        aiTaskService.processMessage(gson.toJson(aiMessage));
    }

    public List<ModelResult> getPersonFrontAlgo(String createDate, Long taskId, String imgPath, PcMonitoringDeviceModel pcMonitoringDeviceModel, List<ModelResult> result) {
        // 人体识别算法模型标识
        String person_body_model_name = configService.selectConfigByKey(URLConstant.PERSON_BODY_MODEL_NAME);
        // 人体识别算法模型标签
        String person_body_label = configService.selectConfigByKey(URLConstant.PERSON_BODY_LABEL);

        String pc_http_url = configService.selectConfigByKey(URLConstant.PC_HTTP_URL);
        // 需要人体前置检测的算法模型
        String person_body_pre_model_ids = configService.selectConfigByKey(URLConstant.PERSON_BODY_PRE_MODEL_IDS);
        // 配置模型
        boolean flag = false;
        if (null != person_body_model_name && person_body_model_name.trim().length() != 0 && !person_body_model_name.equals("false")
                && null != person_body_label && person_body_label.trim().length() != 0 && !person_body_label.equals("false")
                && null != person_body_pre_model_ids && person_body_pre_model_ids.trim().length() != 0 && !person_body_pre_model_ids.equals("false")) {
            // 判断需要人体前置检测的算法模型中的是否包含当前模型名称,若包含继续执行,若不包含跳出执行
            // 默认当前模型不在人体检测前置算法模型中
            boolean checkROIResult = false;
            boolean needCheckForFirstResult = true;
            String[] pre_model_ids = person_body_pre_model_ids.split(",");
            for (String pre_model : pre_model_ids) {
                if (pre_model.contains("&")){
                    checkROIResult = true;
                    needCheckForFirstResult = false;
                    String[] split = pre_model.split("&");
                    pre_model = split[0];
                }
                if (null != pcMonitoringDeviceModel.getAiModelId() && Long.valueOf(pre_model).equals(pcMonitoringDeviceModel.getAiModelId())) {
                    flag = true; // 包含在人体检测前置算法模型中
                    break;
                }
            }
            if (!flag) {
                // 若不包含在,直接返回
                return result;
            } else {
                if (checkROIResult){
                    //检查结果是否符合条件
                    CheckResultAlarmRule checkResultAlarmRule = aiTaskService.checkResultRule(pcMonitoringDeviceModel, result);
                    if (!checkResultAlarmRule.isAlarm()){
                        return null;
                    }
                }
                if (null != result && result.size() >= Integer.parseInt(pcMonitoringDeviceModel.getNumber())) {
                    Long monitoringDeviceId = pcMonitoringDeviceModel.getMonitoringDeviceId();
                    Map<String, Object> stringObjectMap = new HashMap<>();
                    stringObjectMap.put("taskId", taskId);
                    stringObjectMap.put("pcMonitoringDeviceModelId", pcMonitoringDeviceModel.getId());
                    stringObjectMap.put("monitoringDeviceId", monitoringDeviceId);
                    stringObjectMap.put("createDate", createDate);
                    stringObjectMap.put("frontAlgoResult", result);
                    stringObjectMap.put("needCheckForFirstResult",needCheckForFirstResult);
                    getModelResultHttp(imgPath, pc_http_url, person_body_model_name, person_body_label, stringObjectMap);
                }
                return null;
            }
        } else {
            return result;
        }
    }


    public List<ModelResult> getFilterResult(List<ModelResult> personBodyInfo, JSONArray result) {
        logger.info("测试筛选前的结果长度{}*******************************************************{}", result.size(), gson.toJson(result));
        // 人体识别算法模型标识
//        String person_body_model_name = configService.selectConfigByKey(URLConstant.PERSON_BODY_MODEL_NAME);
        // 人体识别算法模型标签
        String person_body_label = configService.selectConfigByKey(URLConstant.PERSON_BODY_LABEL);
        // 人体识别算法模型得分
        String person_body_score = configService.selectConfigByKey(URLConstant.PERSON_BODY_SCORE);
        if (null == person_body_score || person_body_score.trim().length() == 0) {
            person_body_score = "0";
        }
        List<ModelResult> personBodyInfos = new ArrayList<>();
        for (ModelResult o : personBodyInfo) {
//            ModelResult personBodyResult = gson.fromJson(o.toString(), ModelResult.class);
            if (person_body_label.equals(o.getCategory())
                    && o.getScore() >= Double.parseDouble(person_body_score)) {
                personBodyInfos.add(o);
            }
        }
        if (personBodyInfos.size() == 0) {
            return new ArrayList<>();
        }
        //判断当前返回结果是否和框图存在交叉
        List<ModelResult> modelResult = new ArrayList<>();
        for (ModelResult personBodyResult : personBodyInfos) {
            // 人体识别坐标
            List<Double> personBodyBbox = personBodyResult.getBbox();
            // 人体的矩形
            PolygonUtil.Polygon personBodyPolygon = new PolygonUtil.Polygon().setPoints(Arrays.asList(
                    new PolygonUtil.Point(personBodyBbox.get(0), personBodyBbox.get(1)),
                    new PolygonUtil.Point(personBodyBbox.get(2), personBodyBbox.get(1)),
                    new PolygonUtil.Point(personBodyBbox.get(2), personBodyBbox.get(3)),
                    new PolygonUtil.Point(personBodyBbox.get(0), personBodyBbox.get(3))
            ));
            for (Object classNameResult1 : result) {
                ModelResult classNameResult = gson.fromJson(classNameResult1.toString(), ModelResult.class);
                // 调用模型的循环
                if (!"no".equals(classNameResult.getCategory())) {
                    // 调用模型得到的结果坐标
                    List<Double> classNameBbox = classNameResult.getBbox();
                    // 调用模型得到的结果矩形
                    PolygonUtil.Polygon classNamePolygon = new PolygonUtil.Polygon().setPoints(Arrays.asList(
                            new PolygonUtil.Point(classNameBbox.get(0), classNameBbox.get(1)),
                            new PolygonUtil.Point(classNameBbox.get(2), classNameBbox.get(1)),
                            new PolygonUtil.Point(classNameBbox.get(2), classNameBbox.get(3)),
                            new PolygonUtil.Point(classNameBbox.get(0), classNameBbox.get(3))
                    ));
                    if (PolygonUtil.intersectionJudgment(classNamePolygon, personBodyPolygon)) {
//                                    logger.info("模型识别矩形与人体识别矩形相交。");
                        modelResult.add(classNameResult);
                    }
                }
            }
        }
        // 去重
        if (modelResult.size() != 0 && modelResult.size() != 1) {
            for (int i = 0; i < modelResult.size(); i++) {
                for (int j = i + 1; j < modelResult.size(); j++) {
                    BigDecimal score1 = BigDecimal.valueOf(modelResult.get(i).getScore());
                    BigDecimal score2 = BigDecimal.valueOf(modelResult.get(j).getScore());
                    boolean isEqual = modelResult.get(i).getCategory().equals(modelResult.get(j).getCategory()) && modelResult.get(i).getBbox().equals(modelResult.get(j).getBbox()) &&
                            score1.compareTo(score2) == 0;
                    if (isEqual) {
                        modelResult.remove(j);
                        j--;
                    }
                }
            }
        }
        logger.info("测试筛选后的结果长度{}*******************************************************{}", modelResult.size(), gson.toJson(modelResult));
        return modelResult;
    }

    public void getModelResultHttp(String imgPath, String pc_http_url, String person_body_model_name, String person_body_label, Map<String, Object> inferParameter) {
        JSONObject param = new JSONObject();
        param.put("imagePath", imgPath);
        param.put("imageName", imgPath.substring(imgPath.lastIndexOf("/") + 1).replace(".jpg", ""));
        param.put("inferParameter", inferParameter);
        List<ModelNames> modelNamesList = new ArrayList<>();
        ArrayList<String> stringArrayList = new ArrayList<>(Arrays.asList(person_body_label.split(",")));
        ModelNames modelNames = new ModelNames(person_body_model_name, stringArrayList, "");
        modelNamesList.add(modelNames);
        param.put("modelNames", modelNamesList);
        try {
//            logger.info("前置算法参数*******************************************************{}", gson.toJson(param));
            String resultPersonAlgo = OkHttpUtil.post(pc_http_url, param.toString());
//            logger.info("调用底层接口返回值*******************************************************{}", gson.toJson(resultPersonAlgo));
        } catch (Exception e) {
            logger.error("前置算法调用错误->{}", e.getMessage());
        }
    }
}
文档更新时间: 2023-12-18 03:25   作者:JeffreyCheung