Riku
2025-05-14 cf160e28026ed1ed8bea82701d66e79a1085c503
1. 新增走航动态溯源功能
已修改14个文件
已添加3个文件
525 ■■■■ 文件已修改
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionAnalysis.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionResult.kt 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionSlideAverageTag.kt 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionTag.kt 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionAnalysis.kt
@@ -14,4 +14,14 @@
     * ç¡®å®šå¼‚常类型
     */
    abstract fun getExceptionType(): ExceptionType
    /**
     * ç”Ÿæˆä¸€æ¡å¼‚常分析结果
     */
    abstract fun newResult(
        start: BaseRealTimeData,
        end: BaseRealTimeData?,
        factor: FactorFilter.SelectedFactor,
        exceptionData: List<BaseRealTimeData>,
    ): Y
}
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -1,15 +1,18 @@
package com.flightfeather.uav.biz.dataanalysis
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
import com.flightfeather.uav.socket.eunm.FactorType
import java.time.Duration
/**
 * è¿žç»­ç±»åž‹çš„异常分析基类,适用于当前数据与相邻数据之间有关联关系的情况
 */
abstract class BaseExceptionContinuous<V : BaseAnalysisConfig, Y : BaseExceptionResult>(config: V) :
    BaseExceptionAnalysis<V, Y>(config) {
abstract class BaseExceptionContinuous<T : ExceptionTag, V : BaseAnalysisConfig, Y : BaseExceptionResult>(
    config: V, private val tagClz: Class<T>
) : BaseExceptionAnalysis<V, Y>(config) {
    companion object {
        // è®°å½•异常数据段时,分别向起始前和末尾后额外记录的数据个数偏移量
@@ -33,17 +36,26 @@
        var exceptionData = mutableListOf<BaseRealTimeData>()
        // æ˜¯å¦å­˜åœ¨å¼‚常
        var existException = false
        var exceptionExisted = false
        fun refreshAfterCheckResult(data: BaseRealTimeData) {
        // å¼‚常结果是否创建
        var exceptionCreated = false
        fun addExceptionData(data: BaseRealTimeData) {
            exceptionExisted = true
            exceptionData.add(data)
        }
        fun refreshWithNextException(data: BaseRealTimeData) {
            sIndex = eIndex
            startData = data
            exceptionData.clear()
//            exceptionData.add(data)
            exceptionExisted = false
            exceptionCreated = false
        }
    }
    protected val tagMap = mutableMapOf<FactorType, Tag>()
    protected val tagMap = mutableMapOf<FactorType, T>()
    // èµ·å§‹æ•°æ®ä¸Žæœ«å°¾æ•°æ®é—´éš”
    open var durationCount = 1
@@ -71,14 +83,14 @@
     * åˆ¤æ–­å¼‚常出现的连续个数是否满足条件
     * @param tag å¼‚常数据对象
     */
    abstract fun judgeExceptionCount(tag: Tag): Boolean
    abstract fun judgeExceptionCount(tag: T): Boolean
    /**
     * å¼‚常数据的截取判断
     * æ˜¯å¦éœ€è¦é™åˆ¶ä¸€ç»„异常数据的长度
     * @return é»˜è®¤ä¸éœ€è¦æˆªå–
     */
    open fun needCut(tag: Tag): Boolean {
    open fun needCut(tag: T): Boolean {
        return false
    }
@@ -87,7 +99,7 @@
        lastData = null
        tagMap.clear()
        config.factorFilter.mainList().forEach { f ->
            tagMap[f] = Tag()
            tagMap[f] = tagClz.newInstance()
        }
    }
@@ -101,27 +113,18 @@
                // èµ·å§‹æ•°æ®
                it.endData = data
                if (it.startData == null) {
                    it.refreshAfterCheckResult(data)
                    it.refreshWithNextException(data)
                }
                // åˆ¤æ–­ç›¸é‚»æ•°æ®æ˜¯å¦è¿žç»­å¹¶ä¸”是否满足异常判断
                if (!isContinue || needCut(it)) {
                    // æ•°æ®ä¸è¿žç»­æ—¶ï¼Œè®°å½•异常情况
                    recordException(s, it, data)
//                    checkResult(s)
//                    if (it.eIndex - it.sIndex >= durationCount) {
//                        it.refreshAfterCheckResult(data)
//                    }
                } else {
                    if (hasException[f] == true) {
                        it.existException = true
                        it.exceptionData.add(data)
                        it.addExceptionData(data)
                    } else {
                        // å¼‚常不再重复出现时,记录异常情况
                        recordException(s, it, data)
//                        checkResult(s)
//                        if (it.eIndex - it.sIndex >= durationCount) {
//                            it.refreshAfterCheckResult(data)
//                        }
                    }
                }
            }
@@ -130,53 +133,62 @@
    }
    override fun onDone() {
        checkResult()
        checkResult(exceptionStatus = ExceptionStatusType.Ended)
    }
    fun recordException(factor: FactorFilter.SelectedFactor, tag: Tag, data: BaseRealTimeData) {
        checkResult(factor)
        if (tag.eIndex - tag.sIndex >= durationCount) {
            tag.refreshAfterCheckResult(data)
        }
    /**
     * å¼‚常结束,记录异常
     */
    fun recordException(factor: FactorFilter.SelectedFactor, tag: T, data: BaseRealTimeData) {
        checkResult(factor, ExceptionStatusType.Ended)
//        if (tag.eIndex - tag.sIndex >= durationCount) {
        tag.refreshWithNextException(data)
//        }
    }
    /**
     * æ£€æŸ¥è¿žç»­å¼‚常结束时,是否符合异常存储条件
     */
    open fun checkResult(factor: FactorFilter.SelectedFactor? = null) {
    open fun checkResult(
        factor: FactorFilter.SelectedFactor? = null,
        exceptionStatus: ExceptionStatusType = ExceptionStatusType.InProgress
    ) {
        val tag = tagMap[factor?.main]
        if (factor != null && tag != null) {
            if (tag.existException && judgeExceptionCount(tag)) {
                onNewException(tag, factor)
            if (tag.exceptionExisted && judgeExceptionCount(tag)) {
                onNewException(tag, factor, exceptionStatus)
            }
        } else {
            config.factorFilter.selectedList.forEach { f ->
                val tag1 = tagMap[f.main] ?: return@forEach
                if (tag1.existException && judgeExceptionCount(tag1)) {
                    onNewException(tag1, f)
                if (tag1.exceptionExisted && judgeExceptionCount(tag1)) {
                    onNewException(tag1, f, exceptionStatus)
                }
            }
        }
    }
    /**
     * æ–°å¢žä¸€æ¡å¼‚常
     * æ–°å¢žæˆ–更新一条异常
     */
    open fun onNewException(tag:Tag, factor: FactorFilter.SelectedFactor) {
        tag.startData?.let {
            resultList.add(newResult(it, lastData, factor, tag.exceptionData))
    open fun onNewException(tag: T, factor: FactorFilter.SelectedFactor, exceptionStatus: ExceptionStatusType) {
        if (tag.startData == null) return
        val ex = newResult(tag.startData!!, lastData, factor, tag.exceptionData)
            .apply { status = exceptionStatus.value }
        // å¼‚常已创建时,更新异常信息
        if (tag.exceptionCreated) {
            // å°†æœ€æ–°çš„异常的guid赋值给ex
            val lastEx = tag.exceptionResult.last()
            ex.guid = lastEx.guid
            tag.exceptionResult.removeLast()
            tag.exceptionResult.add(ex)
        }
        tag.existException = false
        // å¼‚常未创建时,新建异常信息
        else {
            tag.exceptionResult.add(ex)
//            resultList.add(ex)
            tag.exceptionCreated = true
    }
    /**
     * ç”Ÿæˆä¸€æ¡å¼‚常分析结果
     */
    abstract fun newResult(
        start: BaseRealTimeData,
        end: BaseRealTimeData?,
        factor: FactorFilter.SelectedFactor,
        exceptionData: List<BaseRealTimeData>,
    ): Y
    }
}
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt
@@ -1,12 +1,13 @@
package com.flightfeather.uav.biz.dataanalysis
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.domain.entity.BaseRealTimeData
/**
 * è¿žç»­ç±»åž‹çš„异常分析基类,区别于父类的地方在于此种异常只和单个数据本身有关,与相邻数据无关
 */
abstract class BaseExceptionContinuousSingle<V : BaseAnalysisConfig, Y : BaseExceptionResult>(config: V) :
    BaseExceptionContinuous<V, Y>(config) {
abstract class BaseExceptionContinuousSingle<T : ExceptionTag, V : BaseAnalysisConfig, Y : BaseExceptionResult>(config: V, tagClz: Class<T>) :
    BaseExceptionContinuous<T, V, Y>(config, tagClz) {
    override fun onNextData(data: BaseRealTimeData) {
        val isContinue = isContinuous(lastData, data)
@@ -20,20 +21,17 @@
                }
                // åˆ¤æ–­ç›¸é‚»æ•°æ®æ˜¯å¦è¿žç»­å¹¶ä¸”是否满足异常判断
                if (!isContinue || needCut(it)) {
                    checkResult(s)
                    it.refreshAfterCheckResult(data)
                    recordException(s, it, data)
                } else {
                    if (hasException[f] == true) {
                        // ä¿®æ”¹äº†èµ·å§‹æ•°æ®çš„位置,变更为出现异常的该值,而不是原来的出现异常数据的前一个值
                        if (!it.existException) {
                        if (!it.exceptionExisted) {
                            it.sIndex = it.eIndex
                            it.startData = data
                        }
                        it.existException = true
                        it.exceptionData.add(data)
                        it.addExceptionData(data)
                    } else {
                        checkResult(s)
                        it.refreshAfterCheckResult(data)
                        recordException(s, it, data)
                    }
                }
            }
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionResult.kt
@@ -1,9 +1,22 @@
package com.flightfeather.uav.biz.dataanalysis
import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
import java.util.*
/**
 * å¼‚常结果基类
 * @date 2025/5/13
 * @author feiyu02
 */
abstract class BaseExceptionResult {
    // å¼‚常编号
    var guid: String? = null
    // å¼‚常的状态
    var status: Int = ExceptionStatusType.InProgress.value
    init {
        guid = UUID.randomUUID().toString()
    }
}
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt
@@ -4,6 +4,7 @@
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuous
import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.domain.entity.BaseRealTimeData
/**
@@ -12,7 +13,7 @@
 * @author feiyu02
 */
abstract class ExceptionContinuous(config: DataAnalysisConfig) :
    BaseExceptionContinuous<DataAnalysisConfig, ExceptionResult>(config) {
    BaseExceptionContinuous<ExceptionTag, DataAnalysisConfig, ExceptionResult>(config, ExceptionTag::class.java) {
    override fun newResult(
        start: BaseRealTimeData,
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt
@@ -4,6 +4,7 @@
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuousSingle
import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.socket.eunm.FactorType
@@ -13,7 +14,7 @@
 */
@Deprecated("原超标判定逻辑设定的超标值有漏洞,")
class ExceptionDataExceed(config: DataAnalysisConfig) :
    BaseExceptionContinuousSingle<DataAnalysisConfig, ExceptionResult>(config) {
    BaseExceptionContinuousSingle<ExceptionTag, DataAnalysisConfig, ExceptionResult>(config, ExceptionTag::class.java) {
    override fun getExceptionType(): ExceptionType = ExceptionType.TYPE2
@@ -32,7 +33,7 @@
        return res
    }
    override fun judgeExceptionCount(tag: Tag): Boolean {
    override fun judgeExceptionCount(tag: ExceptionTag): Boolean {
        return true
    }
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt
@@ -44,7 +44,7 @@
        var exceptionData = mutableListOf<BaseRealTimeData>()
        // æ˜¯å¦å­˜åœ¨å¼‚常
        var existException = false
        var exceptionExisted = false
        fun refreshAfterCheckResult(data: BaseRealTimeData) {
            // åˆ¤æ–­å¹¶æ›´æ–°èµ·å§‹ç‚¹ä½ç½®
@@ -100,7 +100,7 @@
                if (tempDataList.size == config.changeTrendGroup) {
                    calAvg(f, tempDataList)
                    if (checkSlideAvg(f)) {
                        it.existException = true
                        it.exceptionExisted = true
                        it.exceptionData.add(data)
                    } else {
                        checkResult(s)
@@ -181,27 +181,27 @@
    private fun checkResult(factor: FactorFilter.SelectedFactor? = null) {
        val tag = tagMap[factor?.main]
        if (factor != null && tag != null) {
            if (tag.existException) {
            if (tag.exceptionExisted) {
                tag.startData?.let {
                    resultList.add(newResult(it, lastData, factor, tag.exceptionData))
                }
                tag.existException = false
                tag.exceptionExisted = false
            }
        } else {
            config.factorFilter.selectedList.forEach { f ->
                val tag1 = tagMap[f.main] ?: return@forEach
                if (tag1.existException) {
                if (tag1.exceptionExisted) {
                    tag1.startData?.let {
                        resultList.add(newResult(it, lastData, f, tag1.exceptionData))
                    }
                    tag1.existException = false
                    tag1.exceptionExisted = false
                }
            }
        }
    }
    fun newResult(
    override fun newResult(
        start: BaseRealTimeData,
        end: BaseRealTimeData?,
        factor: FactorFilter.SelectedFactor,
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt
@@ -1,6 +1,7 @@
package com.flightfeather.uav.biz.dataanalysis.exceptiontype
import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.socket.eunm.FactorType
@@ -39,7 +40,7 @@
        return res
    }
    override fun judgeExceptionCount(tag: Tag): Boolean {
    override fun judgeExceptionCount(tag: ExceptionTag): Boolean {
        // é¦–个数据没有前一个数据参照,不算异常值,最后一个数据是判断结束的正常值,因此异常数据个数的计算下标为sIndex和eIndex
        val sIndex = tag.sIndex
        val eIndex = tag.eIndex - 1
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt
@@ -16,10 +16,10 @@
    factorFilter: FactorFilter,
) : BaseAnalysisConfig(factorFilter) {
    // è¿žç»­çªå˜æ•°æ®ä¸ªæ•°
    var mutationNum = 2
    var mutationNum = 3
    // çªå˜çއ
    var mutationRate = .5
    var mutationRate = .2
    // æ±‚滑动平均值的数据组个数
    var changeTrendGroup = 12
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionSlideAverageTag.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.flightfeather.uav.biz.dataanalysis.model
import com.flightfeather.uav.domain.entity.BaseRealTimeData
/**
 * æ»‘动平均值突变异常数据标签
 * @date 2025/5/13
 * @author feiyu02
 */
class ExceptionSlideAverageTag : ExceptionTag() {
    // æ•°æ®ç»„均值的集合
    val avgListReverse = mutableListOf<Pair<Double, Boolean>>()
    fun refreshAfterCheckResult(historyDataList: List<BaseRealTimeData>, changeTrendGroup: Int) {
        if ((eIndex - sIndex + 1) > changeTrendGroup) {
            sIndex = eIndex + 1 - changeTrendGroup
            startData = historyDataList[sIndex]
            exceptionData.clear()
//            exceptionData.addAll(historyDataList.subList(sIndex, eIndex + 1))
            exceptionExisted = false
            exceptionCreated = false
        }
    }
}
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionTag.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.flightfeather.uav.biz.dataanalysis.model
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionResult
import com.flightfeather.uav.domain.entity.BaseRealTimeData
/**
 * å¼‚常数据标签
 * @date 2025/5/13
 * @author feiyu02
 */
open class ExceptionTag {
    // èµ·å§‹æ•°æ®ä¸‹æ ‡
    var sIndex = 0
    // èµ·å§‹æ•°æ®å¯¹è±¡
    var startData: BaseRealTimeData? = null
    // æœ«å°¾æ•°æ®ä¸‹æ ‡
    var eIndex = -1
    // æœ«å°¾æ•°æ®å¯¹è±¡
    var endData: BaseRealTimeData? = null
    // å¼‚常数据段
    var exceptionData = mutableListOf<BaseRealTimeData>()
    // æ˜¯å¦å­˜åœ¨å¼‚常
    var exceptionExisted = false
    // å¼‚常结果是否创建
    var exceptionCreated = false
    var exceptionResult = mutableListOf<BaseExceptionResult>()
    fun addExceptionData(data: BaseRealTimeData){
        exceptionExisted = true
        exceptionData.add(data)
    }
    fun refreshWithNextException(data: BaseRealTimeData) {
        sIndex = eIndex
        startData = data
        exceptionData.clear()
        exceptionExisted = false
        exceptionCreated = false
    }
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt
@@ -15,25 +15,28 @@
    // é™å®šè·ç¦»å†…(单位:米)
    var distanceLimit = 1000
    // é™å®šæ—¶é—´å†…(单位:分钟)
    var timeLimit = 2
    // çªå˜æ•°æ®ä¸ªæ•°
    var mutationNum = 3
    // çªå˜çއ
    var mutationRate = .2
    // æ±‚滑动平均值的数据组个数
    var changeTrendGroup = 12
    // æ»‘动平均值连续
    var changeTrendInterval = 12
    var changeTrendRate = .2
    // æ»‘动平均值变化率异常连续次数
    var changeTrendTimes = 3
    // æº¯æºæœ‰æ•ˆæœ€å¤§æ—¶é—´ï¼ˆå•位:分钟)
    var sourceTraceTimeLimit = 5
    // æº¯æºæœ‰æ•ˆæœ€å¤§è·ç¦»ï¼ˆå•位:米)
    var sourceTraceDistanceLimit = 2000
    // æº¯æºæœ‰æ•ˆæœ€å¤§é£Žé€Ÿï¼ˆå•位:米/秒),5分钟不超过2公里的风速(暂定)
    var sourceTraceWindSpeedLimit = 6.667
    // æº¯æºæ‰©æ•£åç§»è§’度(单位:度)
    var sourceTraceDegOffset = 30.0
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt
@@ -2,6 +2,7 @@
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis
import com.flightfeather.uav.biz.sourcetrace.exceptiontype.RealTimeExceptionSlideAverage
import com.flightfeather.uav.biz.sourcetrace.exceptiontype.RealTimeExceptionValueMutation
import com.flightfeather.uav.biz.sourcetrace.model.RealTimeExceptionResult
import com.flightfeather.uav.common.utils.GsonUtils
@@ -60,6 +61,9 @@
            add(
                RealTimeExceptionValueMutation(config) { exceptionCallback(it) }.also { it.init() }
            )
            add(
                RealTimeExceptionSlideAverage(config){ exceptionCallback(it)}.also { it.init() }
            )
        }
    }
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
@@ -2,9 +2,11 @@
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuous
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig
import com.flightfeather.uav.biz.sourcetrace.model.RealTimeExceptionResult
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
// å¼‚常数据生成回调类
typealias NewExceptionCallback = (ex: RealTimeExceptionResult) -> Unit
@@ -15,10 +17,10 @@
 * @date 2025/5/13
 * @author feiyu02
 */
abstract class BaseRealTimeException(config: RealTimeAnalysisConfig) :
    BaseExceptionContinuous<RealTimeAnalysisConfig, RealTimeExceptionResult>(config) {
abstract class BaseRealTimeException<T : ExceptionTag>(config: RealTimeAnalysisConfig, tagClz: Class<T>) :
    BaseExceptionContinuous<T, RealTimeAnalysisConfig, RealTimeExceptionResult>(config, tagClz) {
    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback) : this(config){
    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback, tagClz: Class<T>) : this(config, tagClz){
        this.callback = callback
    }
@@ -34,19 +36,18 @@
                // èµ·å§‹æ•°æ®
                it.endData = data
                if (it.startData == null) {
                    it.refreshAfterCheckResult(data)
                    it.refreshWithNextException(data)
                }
                // åˆ¤æ–­ç›¸é‚»æ•°æ®æ˜¯å¦è¿žç»­æˆ–者是否满足自定义截取条件
                if (!isContinue || needCut(it)) {
                    // è®°å½•异常,结束异常的实时状态播报
                    recordException(s, it, data)
                } else {
                    if (hasException[f] == true) {
                        it.existException = true
                        it.exceptionData.add(data)
                    }
                    // ç§»é™¤äº†çˆ¶ç±»åŽŸæœ‰é€»è¾‘ï¼Œæ”¹ä¸ºå½“æ»¡è¶³å¼‚å¸¸æ¡ä»¶æ—¶ï¼Œéœ€è¦å®žæ—¶æŽ¨é€æ’­æŠ¥å¼‚å¸¸çš„çŠ¶æ€å˜åŒ–ï¼Œä½†ä¸æˆªå–å¼‚å¸¸
                    if (hasException[f] == true) {
                        it.addExceptionData(data)
                    checkResult(s)
                    }
                }
            }
        }
@@ -63,11 +64,11 @@
        return RealTimeExceptionResult(start, end, factor, exceptionData, eType)
    }
    override fun onNewException(tag: Tag, factor: FactorFilter.SelectedFactor) {
        super.onNewException(tag, factor)
    override fun onNewException(tag: T, factor: FactorFilter.SelectedFactor, exceptionStatus: ExceptionStatusType) {
        super.onNewException(tag, factor, exceptionStatus)
        callback?.let { func ->
            val exc = resultList.last()
            func.invoke(exc)
            val exc = tag.exceptionResult.last()
            func.invoke(exc as RealTimeExceptionResult)
        }
    }
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,214 @@
package com.flightfeather.uav.biz.sourcetrace.exceptiontype
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis
import com.flightfeather.uav.biz.dataanalysis.exceptiontype.ExceptionSlideAverage.Tag
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionSlideAverageTag
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig
import com.flightfeather.uav.biz.sourcetrace.model.RealTimeExceptionResult
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
import com.flightfeather.uav.socket.eunm.FactorType
import kotlin.math.abs
/**
 * æ»‘动平均值突变异常
 * @date 2025/5/13
 * @author feiyu02
 */
class RealTimeExceptionSlideAverage : BaseExceptionAnalysis<RealTimeAnalysisConfig, RealTimeExceptionResult> {
    constructor(config: RealTimeAnalysisConfig) : super(config)
    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback) : super(config){
        this.callback = callback
    }
    var callback: NewExceptionCallback? = null
    private val historyDataList = mutableListOf<BaseRealTimeData>()
    private val tempDataList = mutableListOf<BaseRealTimeData>()
    private var lastData: BaseRealTimeData? = null
    protected val tagMap = mutableMapOf<FactorType, ExceptionSlideAverageTag>()
    override fun init() {
        super.init()
        historyDataList.clear()
        tempDataList.clear()
        lastData = null
        tagMap.clear()
        config.factorFilter.mainList().forEach { f ->
            tagMap[f] = ExceptionSlideAverageTag()
        }
    }
    override fun getExceptionType(): ExceptionType {
        return ExceptionType.TYPE7
    }
    override fun onNextData(data: BaseRealTimeData) {
        historyDataList.add(data)
        // æ•°æ®åŠ å…¥ä¸´æ—¶æ•°ç»„
        tempDataList.add(data)
        // æ•°æ®é‡è¶…出设置数量时,去除当前数据组首个数据
        if (tempDataList.size > config.changeTrendGroup) {
            tempDataList.removeAt(0)
        }
        config.factorFilter.selectedList.forEach { s ->
            val f = s.main
            tagMap[f]?.let {
                it.eIndex++
                it.endData = lastData
                if (it.startData == null) {
                    it.startData = data
                }
                // æ•°æ®é‡ç­‰äºŽè®¾ç½®æ•°é‡æ—¶ï¼Œè®¡ç®—当前数据组均值
                if (tempDataList.size == config.changeTrendGroup) {
                    calAvg(f, tempDataList)
                    if (checkSlideAvg(f)) {
                        it.addExceptionData(data)
                        checkResult(s)
                    } else {
                        recordException(s, it, data)
                    }
                }
            }
        }
        lastData = data
    }
    override fun onDone() {
        checkResult(exceptionStatus = ExceptionStatusType.Ended)
    }
    /**
     * å¼‚常结束,记录异常
     */
    fun recordException(factor: FactorFilter.SelectedFactor, tag: ExceptionSlideAverageTag, data: BaseRealTimeData) {
        checkResult(factor, ExceptionStatusType.Ended)
        tag.refreshAfterCheckResult(historyDataList, config.changeTrendGroup)
    }
    /**
     * å½“前数据未出现异常时,或数据循环结束时,判断后续步骤
     */
    private fun checkResult(
        factor: FactorFilter.SelectedFactor? = null,
        exceptionStatus: ExceptionStatusType = ExceptionStatusType.InProgress
    ) {
        val tag = tagMap[factor?.main]
        if (factor != null && tag != null) {
            if (tag.exceptionExisted) {
                onNewException(tag, factor, exceptionStatus)
            }
        } else {
            config.factorFilter.selectedList.forEach { f ->
                val tag1 = tagMap[f.main] ?: return@forEach
                if (tag1.exceptionExisted) {
                    onNewException(tag1, f, exceptionStatus)
                }
            }
        }
    }
    /**
     * æ–°å¢žæˆ–更新一条异常
     */
    open fun onNewException(tag: ExceptionSlideAverageTag, factor: FactorFilter.SelectedFactor, exceptionStatus: ExceptionStatusType) {
        if (tag.startData == null) return
        val ex = newResult(tag.startData!!, lastData, factor, tag.exceptionData)
            .apply { status = exceptionStatus.value }
        // å¼‚常已创建时,更新异常信息
        if (tag.exceptionCreated) {
            // å°†æœ€æ–°çš„异常的guid赋值给ex
            val lastEx = tag.exceptionResult.last()
            ex.guid = lastEx.guid
            tag.exceptionResult.removeLast()
            tag.exceptionResult.add(ex)
        }
        // å¼‚常未创建时,新建异常信息
        else {
            tag.exceptionResult.add(ex)
            tag.exceptionCreated = true
        }
        callback?.let { func ->
            val exc = tag.exceptionResult.last()
            func.invoke(exc as RealTimeExceptionResult)
        }
    }
    override fun newResult(
        start: BaseRealTimeData,
        end: BaseRealTimeData?,
        factor: FactorFilter.SelectedFactor,
        exceptionData: List<BaseRealTimeData>,
    ): RealTimeExceptionResult {
        val eType = getExceptionType()
        return RealTimeExceptionResult(start, end, factor, exceptionData, eType)
    }
    /**
     * è®¡ç®—一组数据的均值
     */
    private fun calAvg(type: FactorType, list: List<BaseRealTimeData>) {
        var total = .0
        var valid = true
        val count = list.size
        if (count == 0) return
        list.forEach {
            val v = it.getByFactorType(type)
            if (v == null) {
                valid = false
            } else {
                total += v
            }
        }
        val avg = total / count
        tagMap[type]?.avgListReverse?.add(0, Pair(avg, valid))
    }
    /**
     * è®¡ç®—数据组之间的均值差异是否连续超过限定比率
     */
    private fun checkSlideAvg(type: FactorType): Boolean {
        val tag = tagMap[type] ?: return false
        // è®¡ç®—滑动均值最低要求个数
        val minSize = config.changeTrendTimes + config.changeTrendInterval
        if (tag.avgListReverse.size < minSize) {
            return false
        } else {
            // æ»‘动均值满足数量时,计算均值之间是否连续超过限定比率
            val rateList = mutableListOf<Pair<Double, Boolean>>()
            for (i in tag.avgListReverse.indices) {
                if (i >= config.changeTrendTimes) break
                val r = calAvgChangeRate(tag.avgListReverse[i], tag.avgListReverse[i + config.changeTrendInterval])
                rateList.add(r)
            }
            for (y in rateList) {
                if (!y.second || y.first < config.changeTrendRate) {
                    return false
                }
            }
            return true
        }
    }
    /**
     * è®¡ç®—滑动均值变化率
     * æ±‚a1相对于a2的变化率
     */
    private fun calAvgChangeRate(a1: Pair<Double, Boolean>, a2: Pair<Double, Boolean>): Pair<Double, Boolean> {
        val valid = a1.second && a2.second
        return if (a2.first == .0) {
            Pair(1.0, valid)
        } else {
            Pair(abs(a1.first - a2.first) / a2.first, valid)
        }
    }
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
@@ -1,5 +1,6 @@
package com.flightfeather.uav.biz.sourcetrace.exceptiontype
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig
import com.flightfeather.uav.common.utils.MapUtil
@@ -14,11 +15,11 @@
 * @date 2025/5/13
 * @author feiyu02
 */
class RealTimeExceptionValueMutation : BaseRealTimeException {
class RealTimeExceptionValueMutation : BaseRealTimeException<ExceptionTag> {
    constructor(config: RealTimeAnalysisConfig) : super(config)
    constructor(config: RealTimeAnalysisConfig) : super(config, ExceptionTag::class.java)
    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback) : super(config, callback)
    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback) : super(config, callback, ExceptionTag::class.java)
    /**
     * æœ¬å¼‚常的连续发生次数会根据异常的程度变化
@@ -51,18 +52,16 @@
        return res
    }
    override fun judgeExceptionCount(tag: Tag): Boolean {
        // é¦–个数据没有前一个数据参照,不算异常值,最后一个数据是判断结束的正常值,因此异常数据个数的计算下标为sIndex和eIndex - 1
        val sIndex = tag.sIndex
        val eIndex = tag.eIndex - 1
    override fun judgeExceptionCount(tag: ExceptionTag): Boolean {
        val count = tag.exceptionData.size
        val b1 = special && (eIndex - sIndex) >= (config.mutationNum / 2)
        val b2 = (eIndex - sIndex) >= config.mutationNum
        val b1 = special && count >= (config.mutationNum / 2)
        val b2 = count >= config.mutationNum
        special = false
        return b1 || b2
    }
    override fun needCut(tag: Tag): Boolean {
    override fun needCut(tag: ExceptionTag): Boolean {
        // æŒ‰ç…§æ—¶é•¿å’Œè·ç¦»é™åˆ¶å°†å¼‚常截取
        if (tag.exceptionData.isEmpty()) return false
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt
@@ -11,6 +11,7 @@
import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
import com.flightfeather.uav.socket.eunm.FactorType
import java.math.BigDecimal
import java.util.UUID
/**
 *
@@ -18,11 +19,6 @@
 * @author feiyu02
 */
class RealTimeExceptionResult() : BaseExceptionResult() {
    // å¼‚常编号
    var guid: String? = null
    // å¼‚常的状态
    var status:Int = ExceptionStatusType.InProgress.value
    var deviceCode: String? = null