2025.7.3
1. 新增动态污染溯源新的判定逻辑
| | |
| | | } |
| | | |
| | | /** |
| | | * å¤ææ°æ®é级å¨å¼å¸¸å¤æçèå´å
|
| | | * é»è®¤ææé级é½å¨å¼å¸¸å¤æçèå´å
|
| | | */ |
| | | 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> |
| | |
| | | * @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 |
| | | } |
| | | |
| | | /** |
| | | * å¼å¸¸æ°æ®çæªå夿 |
| | |
| | | |
| | | 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 { |
| | |
| | | // 3. æ°æ®æ£å¸¸ï¼æ ä»»ä½å¼å¸¸æ¶d |
| | | // TODO("2025.6.3ï¼å
¶ä»åç±»çæ¤å¤å·æ°é»è¾å¾
宿â) |
| | | else { |
| | | it.refreshWithNoException(data) |
| | | it.refreshWithNextException(data) |
| | | } |
| | | } |
| | | } |
| | |
| | | } 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) |
| | | } |
| | | } |
| | |
| | | |
| | | 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 { |
| | |
| | | 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, "å¿«éä¸é") |
| | | } |
| | |
| | | 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() }) |
| | | } |
| | | } |
| | | |
| | |
| | | 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) |
| | |
| | | // 宿¶çº¿ç´¢åææ¶é´é´é(åä½ï¼åé) |
| | | var analysisPeriod = 15 |
| | | // 宿¶åæé´éä¸ï¼ç«å³è¿è¡çº¿ç´¢åæçæå°çº¿ç´¢é(åä½ï¼ä¸ª) |
| | | var analysisCount = 2 |
| | | var analysisCount = 4 |
| | | |
| | | /****æ°æ®çªå*****************************************************************************/ |
| | | // 0 - 1çº§é£ |
| | |
| | | 1.6 to 7.9, |
| | | 0.2 to Double.MAX_VALUE, |
| | | DistanceType.TYPE3, |
| | | // 3 |
| | | 1 |
| | | 3 |
| | | // 1 |
| | | ) |
| | | |
| | | // 5 - 6çº§é£ |
| | |
| | | 3 |
| | | ) |
| | | |
| | | /****æ°æ®ååéç*****************************************************************************/ |
| | | /****æ°æ®å¿«éä¸å*****************************************************************************/ |
| | | var changeRateCondition = WindLevelCondition( |
| | | .0 to Double.MAX_VALUE, |
| | | 0.1 to Double.MAX_VALUE, |
| | |
| | | 3 |
| | | ) |
| | | // çæµå åå¨ä¸ä¸ªçæµå¨æï¼4ç§ï¼å
æ£å¸¸ååçé级èå´ |
| | | var changeRate = mutableMapOf( |
| | | var changeRateUp = mutableMapOf( |
| | | FactorType.PM25 to WindLevelCondition( |
| | | .0 to Double.MAX_VALUE, |
| | | 4.0 to Double.MAX_VALUE, |
| | |
| | | 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 |
| | | ), |
| | | ) |
| | | } |
| | |
| | | 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) |
| | |
| | | * @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){ |
| | |
| | | |
| | | 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> { |
| | |
| | | return@forEach |
| | | } |
| | | |
| | | val rate = config.changeRate[f] |
| | | val rate = changeRate[f] |
| | | |
| | | val pValue = p.getByFactorType(f)!! |
| | | val nValue = n.getByFactorType(f)!! |
| | |
| | | val v = (nValue - pValue) |
| | | |
| | | val b1 = if (rate != null) { |
| | | v >= rate.mutationRate.first |
| | | v in rate.mutationRate.first..rate.mutationRate.second |
| | | } else { |
| | | false |
| | | } |
| | |
| | | } |
| | | |
| | | 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 { |
| | |
| | | } |
| | | |
| | | 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( |
¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 |
| | | } |
| | |
| | | historyDataList.addAll(historyData.map { it.toDataVo() }) |
| | | |
| | | calPer() |
| | | calRate() |
| | | } |
| | | |
| | | var deviceCode: String? = null |
| | |
| | | var percentage: Double? = null |
| | | // å åé级平åååå¹
度 |
| | | var avgPer: Double? = null |
| | | // å åé级平åååéç |
| | | var avgRate: Double? = null |
| | | |
| | | // åçæ¬¡æ° |
| | | var times: Int? = null |
| | |
| | | } |
| | | 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) |
| | | } |
| | | } |
| | |
| | | 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) |
| | | } |
| | | } |
| | |
| | | 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() |
| | | // 建议对åºçæ°æ®éæ ·æ¶é´ |
| | |
| | | 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) { |
| | |
| | | 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) { |
| | |
| | | else -> null |
| | | } |
| | | |
| | | fun getVMax(name: String?): Double? { |
| | | fun getVMin(name: String?): Double? { |
| | | getByName(name)?.let { |
| | | return getVMax(it) |
| | | return getVMin(it) |
| | | } |
| | | return null |
| | | } |
| | |
| | | /** |
| | | * ä¸å¤çä½äºæ¤å¼çå¼ |
| | | */ |
| | | fun getVMax(type: FactorType): Double? = when (type) { |
| | | fun getVMin(type: FactorType): Double = when (type) { |
| | | NO -> 1.0 |
| | | NO2 -> 10.0 |
| | | CO -> 100.0 |
| | |
| | | WIND_SPEED -> 2.0 |
| | | WIND_DIRECTION -> 0.0 |
| | | HEIGHT -> 0.0 |
| | | else -> null |
| | | } |
| | | |
| | | /** |
| | |
| | | * @see [PollutedSummary.AnalysisResult] |
| | | */ |
| | | AnaResult(2), |
| | | |
| | | // æ°æ®ååæéï¼éå¼å¸¸ï¼ |
| | | DataChange(3), |
| | | } |
| | |
| | | |
| | | println("${period?.first};${period?.second};${period?.third}") |
| | | } |
| | | |
| | | @Test |
| | | fun foo18() { |
| | | println(-4.382398 in 4.0..Double.MAX_VALUE) |
| | | } |
| | | } |