From 8eb584869b4fd4de0f51c93f2616f12e51df9193 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期四, 18 十二月 2025 17:48:29 +0800
Subject: [PATCH] 2025.12.18 1. 动态溯源相关分析逻辑调整; 2. 走航报告接口参数调整;

---
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt |  339 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 252 insertions(+), 87 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
index 5661859..0483664 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
@@ -1,11 +1,16 @@
 package com.flightfeather.uav.biz.sourcetrace.model
 
 import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
 import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
 import com.flightfeather.uav.common.utils.DateUtil
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.avg
 import com.flightfeather.uav.lightshare.bean.DataVo
+import com.flightfeather.uav.socket.eunm.FactorType
+import java.util.Date
+import kotlin.math.round
 
 /**
  * 姹℃煋鏁版嵁
@@ -13,117 +18,277 @@
  * @author feiyu02
  */
 class PollutedData() {
+    
+    companion object {
+        // 榛樿鏁版嵁閲囨牱鏃堕棿闂撮殧锛屽崟浣嶏細绉�
+        const val DEFAULT_PERIOD = 4
+    }
 
     /**
-     * 9. 鍏宠仈鍥犲瓙
-     * 	a) pm2.5銆乸m10鐗瑰埆楂橈紝涓よ�呭湪鍚勬儏鍐典笅鍚屾灞曠ず锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝姣旈噸瓒婇珮锛岃秺鏈夊彲鑳芥槸椁愰ギ
-     * 	b) pm10鐗瑰埆楂樸�乸m2.5杈冮珮锛屽ぇ棰楃矑鎵皹姹℃煋锛屽彧灞曠ずpm10锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝宸ュ湴涓轰富
-     * 	c) VOC杈冮珮锛屽悓姣旇绠梡m2.5鐨勯噺绾э紝鍙兘瀛樺湪鍚屾鍋忛珮锛堟苯淇�佸姞娌圭珯锛�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
-     * 	d) VOC杈冮珮锛屽浜庡姞娌圭珯锛堣溅杈嗘嫢鍫垫儏鍐碉級锛孋O涓�鑸緝楂�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
-     * 	e) 姘哀鍖栧悎鐗╋紝涓�鑸敱浜庢満鍔ㄨ溅灏炬皵锛屽悓姝ヨ绠桟O
+     * 寮傚父鏁版嵁鍒嗙粍鎯呭喌缁熻
      */
+    inner class ExcGroup{
+        constructor(dataIndexList: List<Int>, factorType: FactorType){
+            this.dataIndexList = dataIndexList
+            this.factorType = factorType
+            val first = getFirstDataValue()?.toDouble()
+            val last = getLastDataValue()?.toDouble()
+            if (first != null && last != null) {
+                per = round((last - first) / first * 100) / 100
+                rate = round((last - first) / DEFAULT_PERIOD * 100) / 100
+            }
+        }
+        var factorType: FactorType? = null
+        /**
+         * 寮傚父鏁版嵁瀵瑰簲鍘嗗彶鏁版嵁[historyDataList]涓殑绱㈠紩鍊�
+         */
+        var dataIndexList: List<Int>? = null
+        // 鍙樺寲骞呭害
+        var per: Double? = null
+        // 鍙樺寲閫熺巼
+        var rate: Double? = null
 
-    constructor(
-        start: BaseRealTimeData,
-        end: BaseRealTimeData?,
-        factor: FactorFilter.SelectedFactor,
-        exceptionData: List<BaseRealTimeData>,
-        historyData: List<BaseRealTimeData>,
-        eType: ExceptionType,
-        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
-    ) : this() {
+        /**
+         * 鑾峰彇寮傚父鏁版嵁鐨勭涓�涓暟鎹�
+         * !!!!绗竴涓暟鎹叾瀹炴槸棣栦釜寮傚父鏁版嵁鐨勫墠涓�涓暟鎹��!!!!
+         */
+        fun getFirstData(): BaseRealTimeData? {
+            return dataIndexList?.firstOrNull()?.let {
+                val i = if (it > 0) it - 1 else it
+                historyDataList[i].toBaseRealTimeData(BaseRealTimeData::class.java)
+            }
+        }
+        fun getFirstDataValue(): Float? {
+            return getFirstData()?.getByFactorType(factorType)
+        }
+
+        /**
+         * 鑾峰彇寮傚父鏁版嵁鐨勬渶鍚庝竴涓暟鎹�
+         */
+        fun getLastData(): BaseRealTimeData? {
+            return dataIndexList?.lastOrNull()?.let {
+                historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java)
+            }
+        }
+        fun getLastDataValue(): Float? {
+            return getLastData()?.getByFactorType(factorType)
+        }
+    }
+
+
+    /**
+     * 鍚勭洃娴嬪洜瀛愬紓甯哥粺璁′俊鎭�
+     */
+    inner class Statistic(){
+        var factorId: Int? = null
+        var factorName: String? = null
+        var subFactorId: List<Int>? = null
+        var subFactorName: List<String>? = null
+        var selectedFactor: FactorFilter.SelectedFactor? = null
+
+        /**
+         * 寮傚父鏁版嵁瀵瑰簲鍘嗗彶鏁版嵁[historyDataList]涓殑绱㈠紩鍊�
+         */
+        var dataIndexList: List<Int>? = null
+
+        // 鍥犲瓙閲忕骇骞冲潎鍙樺寲骞呭害
+        var avgPer: Double? = null
+        // 鍥犲瓙閲忕骇骞冲潎鍙樺寲閫熺巼
+        var avgRate: Double? = null
+
+        var avg: Double? = null
+        var min: Double? = null
+        var max: Double? = null
+
+        var excGroup: List<ExcGroup>? = null
+
+        /**
+         * 鑾峰彇寮傚父鏁版嵁
+         */
+        fun getExceptionData(): List<BaseRealTimeData>? {
+            return dataIndexList?.map { historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java) }
+        }
+
+        /**
+         * 鑾峰彇寮傚父鏁版嵁鍒嗘鎯呭喌
+         * 灏嗚繛缁殑寮傚父鏁版嵁鍒嗕负涓�缁�
+         */
+        fun getExceptionDataGroup(): List<List<Int>> {
+            val res = mutableListOf<MutableList<Int>>()
+            var curGroup = mutableListOf<Int>()
+            var lastIndex = -2
+            dataIndexList?.forEach {
+                if (curGroup.isEmpty()) {
+                    curGroup.add(it)
+                } else if (it - lastIndex == 1) {
+                    curGroup.add(it)
+                } else {
+                    res.add(curGroup)
+                    curGroup = mutableListOf(it)
+                }
+                lastIndex = it
+            }
+            if (curGroup.isNotEmpty()) {
+                res.add(curGroup)
+            }
+            return res
+        }
+    }
+
+    constructor(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>, eType: ExceptionType,) : this() {
+        // 閬嶅巻鎵�鏈夌殑鍥犲瓙鐨勫紓甯革紝鏁村悎缁熶竴鐨勫紓甯哥粨鏋滐紝鍏蜂綋濡備笅
+        var startData: BaseRealTimeData? = null
+        var endData: BaseRealTimeData? = null
+        var historyData = mutableListOf<BaseRealTimeData>()
+        var _times = 0
+        exceptions.forEach { e ->
+            // 灏嗛噰鏍锋椂闂存渶鏃╃殑鏁版嵁浣滀负寮�濮嬫暟鎹�
+            if (startData == null) {
+                startData = e.second.startData
+            } else {
+                if (e.second.startData?.dataTime!! < startData!!.dataTime) {
+                    startData = e.second.startData
+                }
+            }
+
+            // 灏嗛噰鏍锋椂闂存渶鏅氱殑浣滀负缁撴潫鏁版嵁
+            if (endData == null) {
+                endData = e.second.endData
+            } else {
+                if (e.second.endData?.dataTime!! > endData!!.dataTime) {
+                    endData = e.second.endData
+                }
+            }
+            // 灏嗘墍鏈夊巻鍙叉暟鎹幓閲嶅悎骞�
+            if (historyData.isEmpty()) {
+                historyData = e.second.historyData
+            } else {
+                e.second.historyData.forEach {
+                    if (historyData.find { d -> d.dataTime == it.dataTime } == null) {
+                        historyData.add(it)
+                    }
+                }
+            }
+
+            _times += e.second.exceptionData.size
+        }
+        // 鎸夌収閲囨牱鏃堕棿鍗囧簭鎺掑垪
+        historyData.sortBy { it.dataTime }
+
         exception = eType.des
         exceptionType = eType.value
-        factorId = factor.main.value
-        factorName = factor.main.des
-        subFactorId = factor.subs.map { it.value }
-        subFactorName = factor.subs.map { it.des }
-        selectedFactor = factor
-
-        startTime = DateUtil.instance.dateToString(start.dataTime, DateUtil.DateStyle.HH_MM_SS)
-        endTime = DateUtil.instance.dateToString(end?.dataTime, DateUtil.DateStyle.HH_MM_SS) ?: startTime
-//        startData = start.getByFactorType(factor.main)
-//        endData = end?.getByFactorType(factor.main) ?: startData
-        startData = start
-        endData = end
-
-        windSpeed = exceptionData.first().windSpeed?.toDouble()
-        percentage = windLevelCondition?.mutationRate?.first
-        times = windLevelCondition?.countLimit
-
-        dataList.add(start)
-        exceptionData.forEach {
-            dataList.add(it)
-        }
-        dataVoList.addAll(dataList.map { it.toDataVo() })
+        startTime = startData?.dataTime
+        endTime = endData?.dataTime
+        windSpeed = historyData.avg().windSpeed?.toDouble()
+        times = _times
         historyDataList.addAll(historyData.map { it.toDataVo() })
 
-        calPer()
-        calRate()
+        // 鍐嶆鏁村悎寮傚父鏁版嵁锛屽垎鍒绠楀悇鍥犲瓙鐨勫紓甯哥粨鏋滅粺璁�
+        exceptions.forEach {e ->
+            statisticMap[e.first.main] = Statistic().apply {
+                factorId = e.first.main.value
+                factorName = e.first.main.des
+                subFactorId = e.first.subs.map { it.value }
+                subFactorName = e.first.subs.map { it.des }
+                selectedFactor = e.first
+                dataIndexList = e.second.exceptionData.map {
+                    historyDataList.indexOfFirst { d ->
+                        d.time == DateUtil.instance.dateToString(it.dataTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
+                    }
+                }
+
+                val s = dataSummary(e.second.exceptionData, e.first.main)
+                avg = s.first
+                min = s.second
+                max = s.third
+
+                excGroup = getExceptionDataGroup().map { ExcGroup(it, e.first.main) }
+                avgPer = excGroup?.mapNotNull { it.per }?.average()
+                avgRate = excGroup?.mapNotNull { it.rate }?.average()
+            }
+        }
     }
 
     var deviceCode: String? = null
-
     var exception: String? = null
     var exceptionType: Int? = null
-
-    var factorId: Int? = null
-    var factorName: String? = null
-    var subFactorId: List<Int>? = null
-    var subFactorName: List<String>? = null
-    var selectedFactor: FactorFilter.SelectedFactor? = null
-
-    var startTime: String? = null
-    var endTime: String? = null
-
-    var startData: BaseRealTimeData? = null
-    var endData: BaseRealTimeData? = null
-
+    var startTime: Date? = null
+    var endTime: Date? = null
     // 椋庨��
     var windSpeed: Double? = null
-
-    // 鍥犲瓙閲忕骇鍙樺寲骞呭害
-    var percentage: Double? = null
-    // 鍥犲瓙閲忕骇骞冲潎鍙樺寲骞呭害
-    var avgPer: Double? = null
-    // 鍥犲瓙閲忕骇骞冲潎鍙樺寲閫熺巼
-    var avgRate: Double? = null
-
     // 鍙戠敓娆℃暟
     var times: Int? = null
-
     var historyDataList = mutableListOf<DataVo>()
-    // 寮傚父鐩戞祴鏁版嵁
-    var dataList: MutableList<BaseRealTimeData> = mutableListOf()
-    var dataVoList: MutableList<DataVo> = mutableListOf()
+    // 寮傚父鐩戞祴鏁版嵁锛屽寘鍚崟娆″紓甯镐腑鎵�鏈夊彂鐢熶簡寮傚父鐨勬暟鎹�硷紙鍙兘涓嶆槸鏃堕棿杩炵画鐨勬暟鎹級
+//    var dataList: MutableList<BaseRealTimeData> = mutableListOf()
+//    var dataVoList: MutableList<DataVo> = mutableListOf()
+    var statisticMap = mutableMapOf<FactorType, Statistic>()
 
-    private fun calPer() {
-        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) / p
-        }
-        avgPer = total / (list.size - 1)
+    /**
+     * 鑾峰彇鎵�鏈夊紓甯稿洜瀛愬悕绉�
+     */
+    fun toFactorNames(): String {
+        val factors = statisticMap.entries.map { it.key }.sortedBy { it.value }.joinToString(";") { it.des }
+        return factors
     }
 
-    private fun calRate() {
-        val list = dataList
-//        list.add(startData)
-//        list.addAll(dataList)
-        if (list.size < 2) return
+    fun getExceptionAvgData(): BaseRealTimeData {
+        val exceptionDataList = statisticMap.flatMap { it.value.getExceptionData() ?: emptyList() }
+        val avgData = exceptionDataList.avg()
+        return avgData
+    }
+    /**
+     * 鑾峰彇寮傚父鏁版嵁涓績鍧愭爣锛堝紓甯告暟鎹腑缁忓害绾害鐨勫钩鍧囧�硷級
+     */
+    fun getExceptionCenter(): Pair<Double, Double>? {
+        val avgData = getExceptionAvgData()
+        val wgs84Lng = avgData.longitude?.toDouble()
+        val wgs84Lat = avgData.latitude?.toDouble()
+        return if (wgs84Lng == null || wgs84Lat == null) null else Pair(wgs84Lng, wgs84Lat)
+    }
+
+    private fun calPer(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Double? {
+        if (exceptionData.size < 2) return null
 
         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)!!
+        for (i in 0 until exceptionData.size - 1) {
+            val p = exceptionData[i]?.getByFactorType(factorType) ?: .0f
+            val n = exceptionData[i + 1]?.getByFactorType(factorType) ?: .0f
+            total += (n - p) / p
+        }
+        return total / (exceptionData.size - 1)
+    }
+
+    private fun calRate(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Double? {
+        if (exceptionData.size < 2) return null
+
+        var total = .0
+        for (i in 0 until exceptionData.size - 1) {
+            val p = exceptionData[i]?.getByFactorType(factorType) ?: .0f
+            val n = exceptionData[i + 1]?.getByFactorType(factorType) ?: .0f
             total += (n - p) / 4
         }
-        avgRate = total / (list.size - 1)
+        return total / (exceptionData.size - 1)
+    }
+
+    private fun dataSummary(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Triple<Double, Double,
+            Double> {
+        var min = -1.0
+        var max = -1.0
+        var total = .0
+        var count = 0
+        exceptionData.forEach {
+            val value = it?.getByFactorType(factorType)?.toDouble() ?: return@forEach
+            if (min == -1.0 || min > value) {
+                min = round(value * 1000) / 1000
+            }
+            if (max == -1.0 || max < value) {
+                max = round(value * 1000) / 1000
+            }
+            total += value
+            count++
+        }
+        val avg = if (count == 0) .0 else round(total / count * 1000) / 1000
+        return Triple(avg, min, max)
     }
 }
\ No newline at end of file

--
Gitblit v1.9.3