From f1ea263462d1f3f10a886fb083536a38b03a1ceb Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期三, 14 五月 2025 17:33:04 +0800
Subject: [PATCH] 1. 新增动态污染溯源的数据异常判断逻辑 2. 新增动态污染溯源websocket连接功能

---
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt |  162 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 132 insertions(+), 30 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt
index 4bd356a..9fb9090 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt
@@ -4,68 +4,170 @@
 import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis
 import com.flightfeather.uav.biz.sourcetrace.exceptiontype.RealTimeExceptionValueMutation
 import com.flightfeather.uav.biz.sourcetrace.model.RealTimeExceptionResult
-import com.flightfeather.uav.common.api2word.utils.JsonUtils
-import com.flightfeather.uav.common.location.LocationRoadNearby
 import com.flightfeather.uav.common.utils.GsonUtils
+import com.flightfeather.uav.common.utils.MapUtil
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.SceneInfo
 import com.flightfeather.uav.domain.entity.avg
-import com.flightfeather.uav.domain.repository.RealTimeDataRep
-import com.flightfeather.uav.domain.repository.SegmentInfoRep
+import com.flightfeather.uav.domain.repository.SceneInfoRep
+import com.flightfeather.uav.socket.eunm.FactorType
 import com.flightfeather.uav.socket.handler.UnderwayWebSocketServerHandler
-import com.google.gson.Gson
+import com.flightfeather.uav.socket.sender.UnderwayWebSocketSender
+import java.util.Timer
+import java.util.TimerTask
+import kotlin.math.PI
 
 /**
  * 瀹炴椂璧拌埅姹℃煋婧簮
  * @date 2025/5/8
  * @author feiyu02
  */
-class RealTimeExceptionAnalysisController (
-    private val realTimeDataRep: RealTimeDataRep,
-    private val locationRoadNearby: LocationRoadNearby,
-    private val segmentInfoRep: SegmentInfoRep,
-    private val underwayWebSocketServerHandler: UnderwayWebSocketServerHandler,
-    factorFilter: FactorFilter
-){
-    private var config:RealTimeAnalysisConfig = RealTimeAnalysisConfig(factorFilter)
+
+class RealTimeExceptionAnalysisController {
+
+    constructor(sceneInfoRep: SceneInfoRep, factorFilter: FactorFilter?) {
+        this.sceneInfoRep = sceneInfoRep
+        this.config = if (factorFilter != null)
+            RealTimeAnalysisConfig(factorFilter)
+        else
+            RealTimeAnalysisConfig(
+                FactorFilter.builder()
+//                .withMain(FactorType.NO2)
+                    .withMain(FactorType.CO)
+//                .withMain(FactorType.H2S)
+//                .withMain(FactorType.SO2)
+//                .withMain(FactorType.O3)
+                    .withMain(FactorType.PM25)
+                    .withMain(FactorType.PM10)
+                    .withMain(FactorType.VOC)
+                    .create()
+            )
+        initTask(config)
+    }
+
+    constructor(sceneInfoRep: SceneInfoRep) : this(sceneInfoRep, null)
+
+
+    private val sceneInfoRep: SceneInfoRep
+
+    private val config: RealTimeAnalysisConfig
 
     private val taskList = mutableListOf<BaseExceptionAnalysis<RealTimeAnalysisConfig, RealTimeExceptionResult>>()
 
     private fun initTask(config: RealTimeAnalysisConfig) {
         taskList.clear()
         taskList.apply {
-            add(RealTimeExceptionValueMutation(config){ exceptionCallback(it)})
+            add(
+                RealTimeExceptionValueMutation(config) { exceptionCallback(it) }.also { it.init() }
+            )
         }
+
     }
 
-    init {
-        initTask(config)
-    }
-
-    // 璁$畻鍘嗗彶浠诲姟
+    /**
+     * 璁$畻鏂扮殑涓�鏉″疄鏃惰蛋鑸暟鎹�
+     */
     fun addOneData(data: BaseRealTimeData) {
-        taskList
-
+        // 璁$畻寮傚父
+        taskList.forEach { it.onNextData(data) }
+        // 闄愬畾鏃堕棿鍐呮病鏈夋柊鏁版嵁浼犲叆锛屽垯缁撴潫褰撳墠鐨勮绠�
     }
 
+    /**
+     * 瓒呮椂澶勭悊锛屾湁涓ょ瓒呮椂鎯呭喌
+     * 1. 杈冪煭鏃堕棿鍐咃紝涓诲姩缁撴潫杩炵画褰撳墠寮傚父鍒ゆ柇
+     * 2. 杈冮暱鏃堕棿鍐咃紝杩涜鍒濆鍖栨搷浣�
+     */
+    private fun dealOnTimeout() {
+        val timer = Timer(true)
+        timer.schedule(object : TimerTask() {
+            override fun run() {
+                TODO("Not yet implemented")
+            }
+        }, 60 * 1000)
+        timer.cancel()
+    }
+
+    // 鏁版嵁绐佸彉寮傚父鍥炶皟
     private fun exceptionCallback(ex: RealTimeExceptionResult) {
-        if (sourceTrace(ex, config)) {
-            underwayWebSocketServerHandler.broadcast(GsonUtils.gson.toJson(ex))
-        }
+        // 婧簮姹℃煋婧愪俊鎭�
+        sourceTrace(ex, config)
+        // 骞挎挱姹℃煋婧簮寮傚父缁撴灉
+        UnderwayWebSocketSender.broadcast(GsonUtils.gson.toJson(ex))
     }
 
-    private fun sourceTrace(ex: RealTimeExceptionResult, config: RealTimeAnalysisConfig):Boolean {
+    /**
+     * 姹℃煋鍙嶅悜婧簮
+     */
+    private fun sourceTrace(ex: RealTimeExceptionResult, config: RealTimeAnalysisConfig) {
+        // 璁$畻寮傚父鏁版嵁鍧囧��
         val avgData = ex.dataList.avg()
         if (avgData.windSpeed!! > config.sourceTraceWindSpeedLimit) {
-            return false
+            return
         }
 
         // 鍙栦腑闂寸偣浣滀负鍙嶅悜婧簮鐨勮捣鐐�
         val midData = ex.dataList[ex.dataList.size / 2]
 
-//        avgData.longitude
-//        avgData.latitude
-//        avgData.windDirection
-        return false
+        // 璁$畻鍙嶅悜婧簮鍖哄煙
+        val polygon = calSector(
+            avgData.windSpeed!!.toDouble(),
+            avgData.windDirection!!.toDouble(),
+            midData.longitude!!.toDouble() to midData.latitude!!.toDouble(),
+            config.sourceTraceTimeLimit,
+            config.sourceTraceDegOffset
+        )
+
+        // 鎸夌収鍖哄煙妫�绱㈠唴閮ㄦ薄鏌撴簮淇℃伅
+
+        // 1. 棣栧厛鎸夌収鍥涜嚦鑼冨洿浠庢暟鎹簱鍒濇绛涢�夋薄鏌撴簮
+        val fb = MapUtil.calFourBoundaries(polygon)
+        val sceneList = sceneInfoRep.findByCoordinateRange(fb)
+        // 2. 鍐嶇簿纭垽鏂槸鍚﹀湪鍙嶅悜婧簮鍖哄煙澶氳竟褰㈠唴閮�
+        val result = mutableListOf<SceneInfo>()
+        sceneList.forEach {
+            // Fixme 2025.5.14: 姹℃煋婧愮殑鍧愭爣鏄珮寰峰湴鍥惧潗鏍囩郴锛堢伀鏄熷潗鏍囩郴锛夛紝鑰岃蛋鑸暟鎹槸WGS84鍧愭爣绯�
+            val point = MapUtil.gcj02ToWgs84(it!!.longitude.toDouble() to it.latitude.toDouble())
+            if (MapUtil.isPointInPolygon(point, polygon)) {
+                result.add(it)
+            }
+        }
+
+        // 鏇存柊涓棿鐐逛俊鎭�
+        ex.midData = avgData.apply {
+            longitude = midData.longitude
+            latitude = midData.latitude
+        }
+        // 鏇存柊婧簮鑼冨洿鍐呯殑姹℃煋鍦烘櫙淇℃伅
+        ex.relatedSceneList = result
+    }
+
+    /**
+     * 鏍规嵁涓績鐐瑰潗鏍囥�侀鍚戝拰椋庨�燂紝浠ュ強缁欏畾鐨勫す瑙掞紝璁$畻浠ヤ腑蹇冪偣鎸夌収椋庡悜椋庨�熷拰鏃堕暱锛屽悜澶栨墿鏁e舰鎴愮殑鎵囧舰鐨勭偣鍧愭爣
+     * @param windSpeed 椋庨�燂紝鍗曚綅锛氱背/绉�
+     * @param windDir 椋庡悜锛屽崟浣嶏細搴�
+     * @param center 涓績鐐瑰潗鏍囩粡绾害
+     * @param durationMin 鏃堕暱锛屽崟浣嶏細鍒嗛挓
+     * @param defaultDegOffset 鎵╂暎鍋忕Щ瑙掑害
+     * @return 澶氳竟褰㈤《鐐瑰潗鏍囬泦鍚�
+     */
+    private fun calSector(
+        windSpeed: Double, windDir: Double, center: Pair<Double, Double>, durationMin: Int,
+        defaultDegOffset: Double = 30.0,
+    ): List<Pair<Double, Double>> {
+
+        val sDeg = windDir - defaultDegOffset
+        val eDeg = windDir + defaultDegOffset
+        val distance = windSpeed * durationMin * 60
+
+        // 宸︿晶锛堥�嗘椂閽堜晶锛夐《鐐�
+        val p1 = MapUtil.getPointByLen(center, distance, sDeg * PI / 180)
+        // 椋庡悜鍙嶅悜椤剁偣
+        val p2 = MapUtil.getPointByLen(center, distance, windDir * PI / 180)
+        // 鍙充晶锛堥『鏃堕拡渚э級椤剁偣
+        val p3 = MapUtil.getPointByLen(center, distance, eDeg * PI / 180)
+
+        return listOf(center, p1, p2, p3)
     }
 
 }
\ No newline at end of file

--
Gitblit v1.9.3