feiyu02
2025-07-03 c56e1e74426238939f229f0005828d05089715ff
2025.7.3
1. 新增动态污染溯源新的判定逻辑
已修改14个文件
已添加1个文件
200 ■■■■ 文件已修改
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionType.kt 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcChangeRate.kt 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTWarnChangeRate.kt 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/EPWDataPrep.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/socket/sender/MsgType.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/Test.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -53,6 +53,16 @@
    }
    /**
     * åˆ¤æ–­æ•°æ®é‡çº§åœ¨å¼‚常判断的范围内
     * é»˜è®¤æ‰€æœ‰é‡çº§éƒ½åœ¨å¼‚常判断的范围内
     */
    open fun judgeDataScale(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
        val res = mutableMapOf<FactorType, Boolean>()
        config.factorFilter.mainList().forEach { f -> res[f] = true }
        return res
    }
    /**
     * åˆ¤æ–­å‰åŽæ•°æ®æ˜¯å¦æ»¡è¶³å¼‚常条件
     */
    abstract fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean>
@@ -62,6 +72,19 @@
     * @param tag å¼‚常数据对象
     */
    abstract fun judgeExceptionCount(tag: T, factorType: FactorType?): Boolean
    /**
     * åˆ¤æ–­ç›‘测因子是否出现异常
     */
    open fun judge(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
        val jds = judgeDataScale(p, n)
        val jex = judgeException(p, n)
        val res = mutableMapOf<FactorType, Boolean>()
        jds.forEach { (t, u) ->
            res[t] = u && jex[t] ?: false
        }
        return res
    }
    /**
     * å¼‚常数据的截取判断
@@ -83,7 +106,7 @@
    override fun onNextData(data: BaseRealTimeData) {
        val isContinue = isContinuous(lastData, data)
        val hasException = judgeException(lastData, data)
        val hasException = judge(lastData, data)
        config.factorFilter.selectedList.forEach { s ->
            val f = s.main
            tagMap[f]?.let {
@@ -114,7 +137,7 @@
                // 3. æ•°æ®æ­£å¸¸ï¼Œæ— ä»»ä½•异常时d
                // TODO("2025.6.3:其他子类的此处刷新逻辑待完成“)
                else {
                    it.refreshWithNoException(data)
                    it.refreshWithNextException(data)
                }
            }
        }
@@ -149,7 +172,7 @@
        } else {
            config.factorFilter.selectedList.forEach { f ->
                val tag1 = tagMap[f.main] ?: return@forEach
                if (tag1.exceptionExisted && judgeExceptionCount(tag1, null)) {
                if (tag1.exceptionExisted && judgeExceptionCount(tag1, f.main)) {
                    onNewException(tag1, f, exceptionStatus)
                }
            }
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt
@@ -11,7 +11,7 @@
    override fun onNextData(data: BaseRealTimeData) {
        val isContinue = isContinuous(lastData, data)
        val hasException = judgeException(lastData, data)
        val hasException = judge(lastData, data)
        config.factorFilter.selectedList.forEach { s ->
            val f = s.main
            tagMap[f]?.let {
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionType.kt
@@ -1,14 +1,15 @@
package com.flightfeather.uav.biz.dataanalysis.model
enum class ExceptionType(val value:Int, val des:String) {
    TYPE0(0, "数据缺失异常"),
    TYPE1(1, "数据超低异常"),
    TYPE0(0, "数据缺失"),
    TYPE1(1, "数据超低"),
    TYPE2(2, "数据超标"),
    TYPE3(3, "数据长时段无波动"),
    TYPE4(4, "量级突变异常"),
    TYPE5(5, "临近超标异常"),
    TYPE6(6, "单日超标次数临近处罚异常"),
    TYPE7(7, "滑动平均值突变异常"),
    TYPE4(4, "量级突变"),
    TYPE5(5, "临近超标"),
    TYPE6(6, "单日超标次数临近处罚"),
    TYPE7(7, "滑动平均值突变"),
    TYPE8(8, "有效率异常"),
    TYPE9(9, "变化速率异常"),
    TYPE9(9, "快速上升"),
    TYPE10(10, "快速下降")
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt
@@ -72,6 +72,7 @@
            add(RTExcWindLevel4(config) { exceptionCallback(it) }.also { it.init() })
            add(RTExcWindLevel6(config) { exceptionCallback(it) }.also { it.init() })
            add(RTExcChangeRate(config) { exceptionCallback(it) }.also { it.init() })
            add(RTWarnChangeRate(config) { dataChangeCallback(it) }.also { it.init() })
        }
    }
@@ -110,6 +111,15 @@
        pollutedSummary.addClue(ex)
    }
    // æ•°æ®å˜åŒ–提醒回调
    private fun dataChangeCallback(ex: PollutedClue) {
        // æº¯æºæ±¡æŸ“源信息
        ex.searchScenes(sceneInfoRep)
        // å¹¿æ’­æ•°æ®å˜åŒ–提醒
        UnderwayWebSocketSender.broadcast(MsgType.DataChange.value, ex)
    }
    private fun summaryCallback(ex: PollutedSummary.AnalysisResult) {
        // å¹¿æ’­æ±¡æŸ“溯源异常结果
        UnderwayWebSocketSender.broadcast(MsgType.AnaResult.value, ex)
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt
@@ -28,7 +28,7 @@
    // å®šæ—¶çº¿ç´¢åˆ†æžæ—¶é—´é—´éš”(单位:分钟)
    var analysisPeriod = 15
    // å®šæ—¶åˆ†æžé—´éš”中,立即进行线索分析的最小线索量(单位:个)
    var analysisCount = 2
    var analysisCount = 4
    /****数据突变*****************************************************************************/
    // 0 - 1级风
@@ -52,8 +52,8 @@
        1.6 to 7.9,
        0.2 to Double.MAX_VALUE,
        DistanceType.TYPE3,
//        3
        1
        3
//        1
    )
    // 5 - 6级风
@@ -64,7 +64,7 @@
        3
    )
    /****数据变化速率*****************************************************************************/
    /****数据快速上升*****************************************************************************/
    var changeRateCondition = WindLevelCondition(
        .0 to Double.MAX_VALUE,
        0.1 to Double.MAX_VALUE,
@@ -72,7 +72,7 @@
        3
    )
    // ç›‘测因子在一个监测周期(4秒)内正常变化的量级范围
    var changeRate = mutableMapOf(
    var changeRateUp = mutableMapOf(
        FactorType.PM25 to WindLevelCondition(
            .0 to Double.MAX_VALUE,
            4.0 to Double.MAX_VALUE,
@@ -92,4 +92,27 @@
            1
        ),
    )
    /****数据快速下降*****************************************************************************/
    // ç›‘测因子在一个监测周期(4秒)内正常变化的量级范围
    var changeRateDown = mutableMapOf(
        FactorType.PM25 to WindLevelCondition(
            .0 to Double.MAX_VALUE,
            -Double.MAX_VALUE to -2.0,
            DistanceType.TYPE1,
            3
        ),
        FactorType.PM10 to WindLevelCondition(
            .0 to Double.MAX_VALUE,
            -Double.MAX_VALUE to -2.0,
            DistanceType.TYPE1,
            3
        ),
        FactorType.VOC to WindLevelCondition(
            .0 to Double.MAX_VALUE,
            -Double.MAX_VALUE to -3.0,
            DistanceType.TYPE1,
            3
        ),
    )
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt
@@ -37,9 +37,22 @@
        return ExceptionType.TYPE4
    }
    override fun judgeDataScale(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
        val res = mutableMapOf<FactorType, Boolean>()
        config.factorFilter.mainList().forEach { f ->
            if (p?.getByFactorType(f) == null || n.getByFactorType(f) == null || n.windSpeed == null) {
                res[f] = (false)
                return@forEach
            }
            val nValue = n.getByFactorType(f)!!
            val minValue = FactorType.getVMin(f)
            res[f] = nValue >= minValue
        }
        return res
    }
    override fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
        val res = mutableMapOf<FactorType, Boolean>()
        println()
        config.factorFilter.mainList().forEach { f ->
            if (p?.getByFactorType(f) == null || n.getByFactorType(f) == null || n.windSpeed == null) {
                res[f] = (false)
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcChangeRate.kt
@@ -19,7 +19,7 @@
 * @date 2025/6/10
 * @author feiyu02
 */
class RTExcChangeRate(config: RTExcWindLevelConfig) :
open class RTExcChangeRate(config: RTExcWindLevelConfig) :
    BaseExceptionContinuous<ExceptionTag, RTExcWindLevelConfig, PollutedClue>(config, ExceptionTag::class.java) {
    constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : this(config){
@@ -28,8 +28,24 @@
    private var callback: NewPolluteClueCallback? = null
    open var changeRate = this.config.changeRateUp
    override fun getExceptionType(): ExceptionType {
        return ExceptionType.TYPE9
    }
    override fun judgeDataScale(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
        val res = mutableMapOf<FactorType, Boolean>()
        config.factorFilter.mainList().forEach { f ->
            if (p?.getByFactorType(f) == null || n.getByFactorType(f) == null || n.windSpeed == null) {
                res[f] = (false)
                return@forEach
            }
            val nValue = n.getByFactorType(f)!!
            val minValue = FactorType.getVMin(f)
            res[f] = nValue >= minValue
        }
        return res
    }
    override fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
@@ -40,7 +56,7 @@
                return@forEach
            }
            val rate = config.changeRate[f]
            val rate = changeRate[f]
            val pValue = p.getByFactorType(f)!!
            val nValue = n.getByFactorType(f)!!
@@ -48,7 +64,7 @@
            val v = (nValue - pValue)
            val b1 = if (rate != null) {
                v >= rate.mutationRate.first
                v in rate.mutationRate.first..rate.mutationRate.second
            } else {
                false
            }
@@ -61,7 +77,7 @@
    }
    override fun judgeExceptionCount(tag: ExceptionTag, factorType: FactorType?): Boolean {
        return tag.exceptionData.size >= (config.changeRate[factorType]?.countLimit ?: 1)
        return tag.exceptionData.size >= (changeRate[factorType]?.countLimit ?: 1)
    }
    override fun needCut(tag: ExceptionTag, hasException: Boolean?): Boolean {
@@ -97,7 +113,7 @@
    }
    override fun newResult(tag: ExceptionTag, factor: FactorFilter.SelectedFactor): PollutedClue {
        return PollutedClue(tag, factor, getExceptionType(), config, config.changeRate[factor.main])
        return PollutedClue(tag, factor, getExceptionType(), config, changeRate[factor.main])
    }
    override fun onNewException(
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTWarnChangeRate.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.flightfeather.uav.biz.sourcetrace.exceptiontype
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
/**
 * æ•°æ®å¿«é€Ÿä¸‹é™æé†’
 * @date 2025/7/3
 * @author feiyu02
 */
class RTWarnChangeRate : RTExcChangeRate {
    constructor(config: RTExcWindLevelConfig):super(config)
    constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
    override fun getExceptionType(): ExceptionType {
        return ExceptionType.TYPE10
    }
    override var changeRate = config.changeRateDown
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
@@ -59,6 +59,7 @@
        historyDataList.addAll(historyData.map { it.toDataVo() })
        calPer()
        calRate()
    }
    var deviceCode: String? = null
@@ -85,6 +86,8 @@
    var percentage: Double? = null
    // å› å­é‡çº§å¹³å‡å˜åŒ–幅度
    var avgPer: Double? = null
    // å› å­é‡çº§å¹³å‡å˜åŒ–速率
    var avgRate: Double? = null
    // å‘生次数
    var times: Int? = null
@@ -108,4 +111,19 @@
        }
        avgPer = total / (list.size - 1)
    }
    private fun calRate() {
        val list = dataList
//        list.add(startData)
//        list.addAll(dataList)
        if (list.size < 2) return
        var total = .0
        for (i in 0 until list.size - 1) {
            val p = list[i]?.getByFactorType(selectedFactor!!.main)!!
            val n = list[i + 1]?.getByFactorType(selectedFactor!!.main)!!
            total += (n - p) / 4
        }
        avgRate = total / (list.size - 1)
    }
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt
@@ -107,18 +107,22 @@
            FactorType.PM25,
            FactorType.PM10,
                -> {
                val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
                val pm10Avg = round(pollutedData.dataList.map { it.pm10!! }.average() * 10) / 10
                // è®¡ç®—异常数据的pm2.5占pm10比重的均值
                val percentageAvg = pollutedData.dataList.map {
                    it.pm25!! / it.pm10!!
                }.average()
                val str =
                    "PM2.5量级为${pm25Avg}μg/m³,PM10量级为${pm25Avg}μg/m³,PM2.5占PM10的比重为${round(percentageAvg * 100)}%"
                return if (percentageAvg > 0.666) {
                    "PM2.5占PM10的比重为${round(percentageAvg * 100)}%,比重较大,污染源以餐饮为主,工地次之" to
                    "${str},比重较大,污染源以餐饮为主,工地次之" to
                            listOf(SceneType.TYPE1, SceneType.TYPE2, SceneType.TYPE3, SceneType.TYPE14, SceneType.TYPE5)
                } else if (percentageAvg < 0.333) {
                    "PM2.5占PM10的比重为${round(percentageAvg * 100)}%,比重较小,属于大颗粒扬尘污染,污染源以工地为主" to
                    "${str},比重较小,属于大颗粒扬尘污染,污染源以工地为主" to
                            listOf(SceneType.TYPE1, SceneType.TYPE2, SceneType.TYPE3, SceneType.TYPE14, SceneType.TYPE5)
                } else {
                    "PM2.5占PM10的比重为${round(percentageAvg * 100)}%,污染源以餐饮、工地为主" to
                    "${str},污染源以餐饮、工地为主" to
                            listOf(SceneType.TYPE1, SceneType.TYPE2, SceneType.TYPE3, SceneType.TYPE14, SceneType.TYPE5)
                }
            }
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt
@@ -168,7 +168,8 @@
            val closetScene = result.sortedSceneList?.first()
            // èµ°èˆªè·¯çº¿è°ƒæ•´å»ºè®®
            result.advice =
                "根据${sT}至${eT}的${clueList.size}条最新污染线索,污染源【${closetScene?.first?.name}】被多次溯源,具有较高污染风险,现提供新的走航推荐路线,可经过该污染源。"
                "根据${sT}至${eT}的${clueList.size}条溯源切片,风险源【" +
                        "${closetScene?.first?.name}】被多次溯源,具有较高污染风险,现提供新的走航推荐路线,可经过该污染源。"
            val lastP = realTimeDataList.last()
            // å»ºè®®å¯¹åº”的数据采样时间
src/main/kotlin/com/flightfeather/uav/model/epw/EPWDataPrep.kt
@@ -82,7 +82,7 @@
                val it = mDataList[i].values?.get(y) ?: continue
                if (!calTypes.contains(it.factorName)) continue
                val vMax = FactorType.getVMax(it.factorName) ?: continue
                val vMax = FactorType.getVMin(it.factorName) ?: continue
                it.factorData ?: continue
                if (it.factorData!! > vMax) {
@@ -150,7 +150,7 @@
        while (i < mDataList.size) {
            for (y in mDataList[i].values?.indices ?: 0..0) {
                val it = mDataList[i].values?.get(y) ?: continue
                val vMax = FactorType.getVMax(it.factorName) ?: continue
                val vMax = FactorType.getVMin(it.factorName) ?: continue
                it.factorData ?: continue
                if (it.factorData!! > vMax) {
src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt
@@ -125,9 +125,9 @@
            else -> null
        }
        fun getVMax(name: String?): Double? {
        fun getVMin(name: String?): Double? {
            getByName(name)?.let {
                return getVMax(it)
                return getVMin(it)
            }
            return null
        }
@@ -135,7 +135,7 @@
        /**
         * ä¸å¤„理低于此值的值
         */
        fun getVMax(type: FactorType): Double? = when (type) {
        fun getVMin(type: FactorType): Double = when (type) {
            NO -> 1.0
            NO2 -> 10.0
            CO -> 100.0
@@ -155,7 +155,6 @@
            WIND_SPEED -> 2.0
            WIND_DIRECTION -> 0.0
            HEIGHT -> 0.0
            else -> null
        }
        /**
src/main/kotlin/com/flightfeather/uav/socket/sender/MsgType.kt
@@ -15,4 +15,7 @@
     * @see [PollutedSummary.AnalysisResult]
     */
    AnaResult(2),
    // æ•°æ®å˜åŒ–提醒(非异常)
    DataChange(3),
}
src/test/kotlin/com/flightfeather/uav/Test.kt
@@ -163,4 +163,9 @@
        println("${period?.first};${period?.second};${period?.third}")
    }
    @Test
    fun foo18() {
        println(-4.382398 in 4.0..Double.MAX_VALUE)
    }
}