feiyu02
2025-08-05 176d7d8283e66ccf63878c9ab823e900df94b748
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -2,10 +2,10 @@
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.biz.sourcetrace.model.RemainException
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
import com.flightfeather.uav.socket.eunm.FactorType
import org.springframework.beans.BeanUtils
import java.time.Duration
/**
@@ -14,6 +14,11 @@
abstract class BaseExceptionContinuous<T : ExceptionTag, V : BaseAnalysisConfig, Y : BaseExceptionResult>(
    config: V, private val tagClz: Class<T>,
) : BaseExceptionAnalysis<V, Y>(config) {
    enum class JudgeMethod(val des: String) {
        M1("在一定的空间和时间范围内,数据累计出现N次异常后,认为该异常成立"),
        M2("要求数据不间断连续出现N次异常后,认为该异常成立"),
    }
    companion object {
        // 记录异常数据段时,分别向起始前和末尾后额外记录的数据个数偏移量
@@ -29,17 +34,27 @@
    protected var lastData: BaseRealTimeData? = null
    // 最新的一组异常,记录单因子异常
    protected val latestExceptions = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>()
    val latestExceptions = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>()
    /**
     * 最新的一组合并异常,根据配置参数从[latestExceptions]单因子异常中,合并异常
     */
    protected val latestCombinedExc = mutableListOf<List<Pair<FactorFilter.SelectedFactor, T>>>()
    // 记录需要延迟数据周期进行合并的异常
    val remainingExceptions = mutableListOf<RemainException<T>>()
    /**
     * 异常结果
     */
    protected val result = mutableListOf<Y>()
    val result = mutableListOf<Y>()
    /**
     * 不适用于此异常类型的监测因子
     */
    open var excludedFactor: List<FactorType> = emptyList()
    abstract var judgeMethod: JudgeMethod
    /**
     * 立即判断:当出现异常时,缓存异常数据的同时,立即对已有异常进行判断是否满足异常结果要求
@@ -98,7 +113,7 @@
     * @return
     */
    open fun needCut(tag: T, hasException: Boolean?, data: BaseRealTimeData): Boolean {
        // 默认判断条件为 当异常不再重复出现时,形成异常结果
        // 默认判断条件为 当异常不再重复出现时
        return tag.exceptionExisted && hasException == false
    }
@@ -116,9 +131,10 @@
        val hasException = judge(lastData, data)
        config.factorFilter.selectedList.forEach { s ->
            val f = s.main
            tagMap[f]?.let {
                it.addHistoryData(data)
            // 排除此异常类型不适用的监测因子
            if (excludedFactor.contains(f)) return@forEach
            tagMap[f]?.let {
                it.eIndex++
                // 起始数据
                it.endData = data
@@ -126,41 +142,87 @@
                    it.refreshWithNextException(data)
                }
                // 对于异常的生成分别执行后置判断、和立即判断
                // 1. 后置判断:当相邻数据时间不连续时,或者满足自定义条件时,对之前已有的异常进行记录,形成异常结果
//                if (afterExcCheck(isContinue, it, hasException[f])) {
//                    // 数据不连续时或者满足主动截断条件时,记录异常情况
//                    recordException(s, it, data)
//                }
                // 2. 立即判断:当出现异常时,缓存异常数据的同时,立即对已有异常进行判断是否满足异常结果要求
                if (hasException[f] == true) {
//                    afterExcCheck(isContinue, it, hasException[f])
                    if (needCut(it, hasException[f], data)) {
                        it.refreshWithNextException(data)
                    }
                    // 有异常出现时,记录异常数据
                    it.addExceptionData(data)
                    // 当立即判断通过时,形成异常结果
                    if (immeExcCheck(it, f)) {
                        recordException(s, it, data)
                    }
                // 按照不同的方式进行异常判断
                when (judgeMethod) {
                    JudgeMethod.M1 -> judgeMethod1(hasException, f, it, data, s)
                    JudgeMethod.M2 -> judgeMethod2(isContinue, hasException, f, it, data, s)
                }
                // 3. 数据正常,无任何异常时d
                // TODO("2025.6.3:其他子类的此处刷新逻辑待完成“)
//                else {
//                    it.refreshWithNextException(data)
//                }
                it.addHistoryData(data)
            }
        }
        lastData = data
        mergeExceptionResult()
        removeSingleFactor(data)
        checkDelayedExceptions(data)
        mergeExceptionResult(data)
        onNewResult(result)
        clearExceptions(data)
    }
    override fun onDone() {
        checkResult(exceptionStatus = ExceptionStatusType.Ended)
    }
    /**
     * 数据异常判断方式一
     * 在一定的空间和时间范围内,数据累计出现N次异常后,认为该异常成立
     */
    private fun judgeMethod1(
        hasException: MutableMap<FactorType, Boolean>,
        f: FactorType,
        tag: T,
        data: BaseRealTimeData,
        s: FactorFilter.SelectedFactor,
    ) {
        // 出现异常
        if (hasException[f] == true) {
            // 判断数据在空间和时间变化上是否超出限定范围,若超出则删除遗留的异常记录,刷新起始点数据
            if (needCut(tag, hasException[f], data)) {
                tag.refreshWithNextException(data)
            }
            // 记录异常数据
            tag.addExceptionData(data)
            // 当立即判断通过时,形成异常结果
            if (immeExcCheck(tag, f)) {
                recordException(s, tag, data)
            }
        }
        // 数据正常,并且没有历史异常数据时,刷新起始点数据
        else if (!tag.exceptionExisted) {
            tag.refreshWithNextException(data)
        }
    }
    /**
     * 数据异常判断方式二
     * 要求数据不间断连续出现N次异常后,认为该异常成立
     */
    private fun judgeMethod2(
        isContinue: Boolean,
        hasException: MutableMap<FactorType, Boolean>,
        f: FactorType,
        tag: T,
        data: BaseRealTimeData,
        s: FactorFilter.SelectedFactor,
    ) {
        // 当相邻数据时间不连续时,刷新起始点数据,移除历史异常记录
        if (!isContinue) {
            tag.refreshWithNextException(data)
        }
        // 出现异常
        else if (hasException[f] == true) {
            // 有异常出现时,记录异常数据
            tag.addExceptionData(data)
            // 当立即判断通过时,形成异常结果
            if (immeExcCheck(tag, f)) {
                recordException(s, tag, data)
            }
        }
        // 数据正常,刷新起始点数据,移除历史异常记录
        else {
            tag.refreshWithNextException(data)
        }
    }
    /**
@@ -221,54 +283,163 @@
    }
    /**
     * 将不在关联关系中的监测因子异常存储,并剔除
     */
    fun removeSingleFactor(data: BaseRealTimeData) {
        // 查找不在因子关联组合中的异常因子
        val sfList = latestExceptions.filter {
            config.factorFilter.combination.find { c -> c.find { f -> f == it.first.main } != null } == null
        }
        // 生成对应的异常结果,并初始化该异常
        sfList.forEach {
            result.add(newResult(listOf(it)))
            it.second.refreshWithNextException(data)
        }
        // 剔除
        latestExceptions.removeAll(sfList)
    }
    /**
     * 检查延迟的待合并异常与当前异常是否能匹配
     * 1. 将遗留的超过等待数据周期的异常存储
     * 2. 将匹配成功的合并异常存储
     * 3. 保留依旧未合并成功并且可继续等待的异常
     * @return 被匹配成功的关联关系
     */
    fun checkDelayedExceptions(data: BaseRealTimeData): List<List<FactorType>> {
        // 被匹配成功的监测因子关联关系
        val fittedComb = mutableListOf<List<FactorType>>()
        // 遗留的进入下一个数据周期做判断的待合并异常集合
        val leftExc = mutableListOf<RemainException<T>>()
        // 成功匹配的合并异常集合
        val combinedExc = mutableListOf<List<Pair<FactorFilter.SelectedFactor, T>>>()
        // 本次数据周期中,被匹配成功的异常集合
        val exceps = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>()
        remainingExceptions.forEach {
            // 检查当前新异常中,是否包含因子关联关系中的异常
            val combRes = matchCombFactor(it.combination, latestExceptions)
            val res = combRes.second
            // 判断本次数据周期中找到的因子和已有的因子是否满足关联关系
            val findFactors = mutableListOf<FactorType>()
            res.forEach {r -> findFactors.add(r.first.main) }
            it.exceptions.forEach {r -> findFactors.add(r.first.main) }
            // 判断是否还有缺失异常
            val isFitAll = findFactors.distinct() == it.combination
            // 如果已经没有缺失的异常因子,则可合并为组合异常
            if (isFitAll) {
                fittedComb.add(it.combination)
                // 将查找结果添加至已有异常集合中
                it.addExceptions(res)
//                // 记录被匹配成功的异常
//                res.forEach { r->
//                    if (exceps.find { e -> e.second == r.second } == null) {
//                        exceps.add(r)
//                    }
//                }
                // 将合并异常存储
                combinedExc.add(it.exceptions)
            }
            // 否则留作下次数据周期再判定存入待合并异常集合
            else {
                it.period++
                // 当待合并的异常等待数据周期大于设定值时,不再等待,直接输出异常
                if (it.period > config.maxDelayPeriod) {
                    result.add(newResult(it.exceptions))
                    return@forEach
                } else {
                    fittedComb.add(it.combination)
                    // 将查找结果添加至已有异常集合中
                    it.addExceptions(res)
//                    // 记录被匹配成功的异常
//                    res.forEach { r->
//                        if (exceps.find { e -> e.second == r.second } == null) {
//                            exceps.add(r)
//                        }
//                    }
                    leftExc.add(it)
                }
            }
        }
        // 存储合并异常
        combinedExc.forEach {
            result.add(newResult(it))
        }
//        // 将被匹配成功的异常刷新,并从本次数据周期的异常集合中移除
//        exceps.forEach { r-> r.second.refreshWithNextException(data) }
//        latestExceptions.removeAll(exceps)
        // 保留未匹配的组合
        remainingExceptions.clear()
        remainingExceptions.addAll(leftExc)
        return fittedComb
    }
    /**
     * 合并异常
     */
    open fun mergeExceptionResult() {
    open fun mergeExceptionResult(data: BaseRealTimeData) {
        val combinedExc = mutableListOf<List<Pair<FactorFilter.SelectedFactor, T>>>()
        // 遍历所有的因子组合
        config.factorFilter.combination.forEach { c ->
            val res = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>()
            var exist = true
            // 查看组合内的所有因子是否都同时出现异常
            c.forEach { f ->
                val r = latestExceptions.find { e ->
                    e.first.main == f
                }
                if (r != null) {
                    res.add(r)
                } else {
                    exist = false
                }
            }
            val combRes = matchCombFactor(c, latestExceptions)
            val res = combRes.second
            val exist = combRes.first
            // 如果组合内的所有因子都存在异常,则存储为合并异常
            if (exist) {
                // 将合并异常从单个异常集合中去除
                res.forEach { r ->
                    latestExceptions.removeIf { e -> e.first.main == r.first.main }
                }
                // 将合并异常存储
                latestCombinedExc.add(res)
                combinedExc.add(res)
            }
            // 否则将异常的深拷贝版本存入待合并异常集合
            // TODO 2025.8.4: 后续添加当关联的监测因子累计异常计数接近阈值时,才存入集合的逻辑
            else {
                remainingExceptions.add(RemainException(res, c))
            }
        }
        // 存储异常结果
        latestExceptions.forEach {
            result.add(newResult(listOf(it)))
        }
        latestCombinedExc.forEach {
        // 存储合并异常
        combinedExc.forEach {
            result.add(newResult(it))
        }
    }
    /**
     * 匹配关联异常因子
     * @param comb 关联因子关系
     * @param exceptions 各监测因子异常集合
     * @return exist表示是否找到关联关系[comb]中所有的因子,res表示找到的结果
     */
    private fun matchCombFactor(
        comb: List<FactorType>,
        exceptions: List<Pair<FactorFilter.SelectedFactor, T>>,
    ): Pair<Boolean, MutableList<Pair<FactorFilter.SelectedFactor, T>>> {
        val res = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>()
        var exist = true
        // 查看组合内的所有因子是否都同时出现异常
        comb.forEach { f ->
            val r = exceptions.find { e ->
                e.first.main == f
            }
            if (r != null) {
                res.add(r)
            } else {
                exist = false
            }
        }
        return exist to res
    }
    abstract fun onNewResult(result: List<Y>)
    /**
     * 在异常生成结果后,进行初始化
     */
    private fun clearExceptions(data: BaseRealTimeData) {
        // 此时latestExceptions中应该包含的依旧是本次数据周期内的所有异常
        latestExceptions.forEach {
            it.second.refreshWithNextException(data)
        }
        latestExceptions.clear()
        latestCombinedExc.forEach {
            it.forEach { e ->
                e.second.refreshWithNextException(data)
            }
        }
        latestCombinedExc.clear()
        result.clear()
    }