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/socket/handler/BaseHandler.kt                                   |    9 
 src/main/kotlin/com/flightfeather/uav/UAVApplication.kt                                               |    2 
 src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt                                  |   11 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt          |   73 +++++
 src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt                        |   36 ++
 src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt                                         |  133 ++++++++
 src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImplTest.kt          |    5 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt          |  162 +++++++++--
 src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt                           |   30 -
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt                     |   33 +-
 src/test/kotlin/com/flightfeather/uav/socket/UnderwayProcessorTest.kt                                 |    9 
 src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt                                       |  120 +++++---
 /dev/null                                                                                             |   43 ---
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt                       |   13 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt |    5 
 src/main/kotlin/com/flightfeather/uav/domain/entity/SceneInfo.java                                    |    6 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt                |   18 +
 src/main/kotlin/com/flightfeather/uav/domain/repository/impl/AirDataRepImpl.kt                        |   41 --
 src/main/kotlin/com/flightfeather/uav/lightshare/eunm/ExceptionStatusType.kt                          |   11 
 src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt                |   53 ++-
 20 files changed, 561 insertions(+), 252 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt b/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt
index c660050..d94f3f2 100644
--- a/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt
+++ b/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt
@@ -23,7 +23,7 @@
 
     @Bean
     fun runner() = ApplicationRunner{
-        underwaySocketServer.startWebSocketServer(9031, underwayProcessor)
+        underwaySocketServer.startWebSocketServer(9031)
         underwaySocketServer.startUnderwayServer(9030, underwayProcessor)
         underwaySocketServer.startElectricServer(9009, electricProcessor)
     }
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
index 52f9cac..0c2e28a 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -105,21 +105,23 @@
                 }
                 // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇
                 if (!isContinue || needCut(it)) {
-                    checkResult(s)
                     // 鏁版嵁涓嶈繛缁椂锛岃褰曞紓甯告儏鍐�
-                    if (it.eIndex - it.sIndex >= durationCount) {
-                        it.refreshAfterCheckResult(data)
-                    }
+                    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)
                     } else {
                         // 寮傚父涓嶅啀閲嶅鍑虹幇鏃讹紝璁板綍寮傚父鎯呭喌
-                        checkResult(s)
-                        if (it.eIndex - it.sIndex >= durationCount) {
-                            it.refreshAfterCheckResult(data)
-                        }
+                        recordException(s, it, data)
+//                        checkResult(s)
+//                        if (it.eIndex - it.sIndex >= durationCount) {
+//                            it.refreshAfterCheckResult(data)
+//                        }
                     }
                 }
             }
@@ -131,6 +133,13 @@
         checkResult()
     }
 
+    fun recordException(factor: FactorFilter.SelectedFactor, tag: Tag, data: BaseRealTimeData) {
+        checkResult(factor)
+        if (tag.eIndex - tag.sIndex >= durationCount) {
+            tag.refreshAfterCheckResult(data)
+        }
+    }
+
     /**
      * 妫�鏌ヨ繛缁紓甯哥粨鏉熸椂锛屾槸鍚︾鍚堝紓甯稿瓨鍌ㄦ潯浠�
      */
@@ -139,20 +148,12 @@
         if (factor != null && tag != null) {
             if (tag.existException && judgeExceptionCount(tag)) {
                 onNewException(tag, factor)
-//                tag.startData?.let {
-//                    resultList.add(newResult(it, lastData, factor, tag.exceptionData))
-//                }
-//                tag.existException = false
             }
         } else {
             config.factorFilter.selectedList.forEach { f ->
                 val tag1 = tagMap[f.main] ?: return@forEach
                 if (tag1.existException && judgeExceptionCount(tag1)) {
                     onNewException(tag1, f)
-//                    tag1.startData?.let {
-//                        resultList.add(newResult(it, lastData, f, tag1.exceptionData))
-//                    }
-//                    tag1.existException = false
                 }
             }
         }
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt
index 6f8a4af..bd28bbb 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt
@@ -25,6 +25,15 @@
     // 绐佸彉鐜�
     var mutationRate = .2
 
-    // 婧簮鏈夋晥鏈�澶ч閫燂紝5鍒嗛挓涓嶈秴杩�2鍏噷鐨勯閫燂紙鏆傚畾锛�
-    var sourceTraceWindSpeedLimit = 6.7
+    // 婧簮鏈夋晥鏈�澶ф椂闂达紙鍗曚綅锛氬垎閽�)
+    var sourceTraceTimeLimit = 5
+
+    // 婧簮鏈夋晥鏈�澶ц窛绂伙紙鍗曚綅锛氱背锛�
+    var sourceTraceDistanceLimit = 2000
+
+    // 婧簮鏈夋晥鏈�澶ч閫燂紙鍗曚綅锛氱背/绉掞級锛�5鍒嗛挓涓嶈秴杩�2鍏噷鐨勯閫燂紙鏆傚畾锛�
+    var sourceTraceWindSpeedLimit = 6.667
+
+    // 婧簮鎵╂暎鍋忕Щ瑙掑害锛堝崟浣嶏細搴︼級
+    var sourceTraceDegOffset = 30.0
 }
\ No newline at end of file
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
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
new file mode 100644
index 0000000..c464995
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
@@ -0,0 +1,73 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuous
+import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig
+import com.flightfeather.uav.biz.sourcetrace.model.RealTimeExceptionResult
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+
+// 寮傚父鏁版嵁鐢熸垚鍥炶皟绫�
+typealias NewExceptionCallback = (ex: RealTimeExceptionResult) -> Unit
+/**
+ * 璧拌埅鍔ㄦ�佹函婧愬紓甯稿垽鏂�
+ * 鐩告瘮浜庡叾鐖剁被锛屼慨鏀逛簡寮傚父鍒嗙粍鐨勯�昏緫锛屼笉鍐嶈�冭檻寮傚父闇�瑕佽繛缁嚭鐜帮紝鑰屾槸鍙�氳繃鏁版嵁閲囨牱鏃堕棿鏄惁杩炵画浠ュ強鏄惁闇�瑕佷富鍔ㄦ埅鍙栧垎缁勬潵鍐冲畾
+ * 鍙﹀鏂板浜嗗紓甯稿疄鏃舵挱鎶ョ殑閫昏緫
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+abstract class BaseRealTimeException(config: RealTimeAnalysisConfig) :
+    BaseExceptionContinuous<RealTimeAnalysisConfig, RealTimeExceptionResult>(config) {
+
+    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback) : this(config){
+        this.callback = callback
+    }
+
+    var callback: NewExceptionCallback? = null
+
+    override fun onNextData(data: BaseRealTimeData) {
+        val isContinue = isContinuous(lastData, data)
+        val hasException = judgeException(lastData, data)
+        config.factorFilter.selectedList.forEach { s ->
+            val f = s.main
+            tagMap[f]?.let {
+                it.eIndex++
+                // 璧峰鏁版嵁
+                it.endData = data
+                if (it.startData == null) {
+                    it.refreshAfterCheckResult(data)
+                }
+                // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画鎴栬�呮槸鍚︽弧瓒宠嚜瀹氫箟鎴彇鏉′欢
+                if (!isContinue || needCut(it)) {
+                    // 璁板綍寮傚父锛岀粨鏉熷紓甯哥殑瀹炴椂鐘舵�佹挱鎶�
+                    recordException(s, it, data)
+                } else {
+                    if (hasException[f] == true) {
+                        it.existException = true
+                        it.exceptionData.add(data)
+                    }
+                    // 绉婚櫎浜嗙埗绫诲師鏈夐�昏緫锛屾敼涓哄綋婊¤冻寮傚父鏉′欢鏃讹紝闇�瑕佸疄鏃舵帹閫佹挱鎶ュ紓甯哥殑鐘舵�佸彉鍖栵紝浣嗕笉鎴彇寮傚父
+                    checkResult(s)
+                }
+            }
+        }
+        lastData = data
+    }
+
+    override fun newResult(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+    ): RealTimeExceptionResult {
+        val eType = getExceptionType()
+        return RealTimeExceptionResult(start, end, factor, exceptionData, eType)
+    }
+
+    override fun onNewException(tag: Tag, factor: FactorFilter.SelectedFactor) {
+        super.onNewException(tag, factor)
+        callback?.let { func ->
+            val exc = resultList.last()
+            func.invoke(exc)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionContinuous.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionContinuous.kt
deleted file mode 100644
index ce7c9b4..0000000
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionContinuous.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.flightfeather.uav.biz.sourcetrace.exceptiontype
-
-import com.flightfeather.uav.biz.FactorFilter
-import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuous
-import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
-import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig
-import com.flightfeather.uav.biz.sourcetrace.model.RealTimeExceptionResult
-import com.flightfeather.uav.domain.entity.BaseRealTimeData
-
-// 寮傚父鏁版嵁鐢熸垚鍥炶皟绫�
-typealias NewExceptionCallback = (ex: RealTimeExceptionResult) -> Unit
-/**
- *
- * @date 2025/5/13
- * @author feiyu02
- */
-abstract class RealTimeExceptionContinuous(config: RealTimeAnalysisConfig) :
-    BaseExceptionContinuous<RealTimeAnalysisConfig, RealTimeExceptionResult>(config) {
-
-    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback) : this(config){
-        this.callback = callback
-    }
-
-    var callback: NewExceptionCallback? = null
-
-    override fun newResult(
-        start: BaseRealTimeData,
-        end: BaseRealTimeData?,
-        factor: FactorFilter.SelectedFactor,
-        exceptionData: List<BaseRealTimeData>,
-    ): RealTimeExceptionResult {
-        val eType = getExceptionType()
-        return RealTimeExceptionResult(start, end, factor, exceptionData, eType)
-    }
-
-    override fun onNewException(tag: Tag, factor: FactorFilter.SelectedFactor) {
-        super.onNewException(tag, factor)
-        callback?.let { func ->
-            val exc = resultList.last()
-            func.invoke(exc)
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
index b87f479..f5eb79f 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
@@ -1,6 +1,5 @@
 package com.flightfeather.uav.biz.sourcetrace.exceptiontype
 
-import com.flightfeather.uav.biz.FactorFilter
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
 import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig
 import com.flightfeather.uav.common.utils.MapUtil
@@ -15,7 +14,7 @@
  * @date 2025/5/13
  * @author feiyu02
  */
-class RealTimeExceptionValueMutation : RealTimeExceptionContinuous {
+class RealTimeExceptionValueMutation : BaseRealTimeException {
 
     constructor(config: RealTimeAnalysisConfig) : super(config)
 
@@ -53,7 +52,7 @@
     }
 
     override fun judgeExceptionCount(tag: Tag): Boolean {
-        // 棣栦釜鏁版嵁娌℃湁鍓嶄竴涓暟鎹弬鐓э紝涓嶇畻寮傚父鍊硷紝鏈�鍚庝竴涓暟鎹槸鍒ゆ柇缁撴潫鐨勬甯稿�硷紝鍥犳寮傚父鏁版嵁涓暟鐨勮绠椾笅鏍囦负sIndex鍜宔Index
+        // 棣栦釜鏁版嵁娌℃湁鍓嶄竴涓暟鎹弬鐓э紝涓嶇畻寮傚父鍊硷紝鏈�鍚庝竴涓暟鎹槸鍒ゆ柇缁撴潫鐨勬甯稿�硷紝鍥犳寮傚父鏁版嵁涓暟鐨勮绠椾笅鏍囦负sIndex鍜宔Index - 1
         val sIndex = tag.sIndex
         val eIndex = tag.eIndex - 1
 
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt
index 86ef036..8f5d0eb 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt
@@ -7,6 +7,8 @@
 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.lightshare.bean.DataVo
+import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
 import com.flightfeather.uav.socket.eunm.FactorType
 import java.math.BigDecimal
 
@@ -16,6 +18,11 @@
  * @author feiyu02
  */
 class RealTimeExceptionResult() : BaseExceptionResult() {
+
+    // 寮傚父缂栧彿
+    var guid: String? = null
+    // 寮傚父鐨勭姸鎬�
+    var status:Int = ExceptionStatusType.InProgress.value
 
     var deviceCode: String? = null
 
@@ -39,10 +46,16 @@
 
     // 寮傚父鏁版嵁锛屽ご灏惧彲鑳藉寘鍚竴瀹氶噺鐨勫亸绉�
     var dataList: MutableList<BaseRealTimeData> = mutableListOf()
+    var dataVoList: MutableList<DataVo> = mutableListOf()
 
     // 涓績鐐圭粡绾害
     var longitude: BigDecimal? = null
     var latitude: BigDecimal? = null
+
+    // 涓棿鏁版嵁鐐瑰潗鏍�
+    var midData: BaseRealTimeData? = null
+//    var midLongitude: BigDecimal? = null
+//    var midLatitude: BigDecimal? = null
 
     // 婧簮浼佷笟
     var relatedSceneList: List<SceneInfo?>? = null
@@ -77,7 +90,10 @@
         min = s.second
         max = s.third
 
-        exceptionData.forEach { dataList.add(it) }
+        exceptionData.forEach {
+            dataList.add(it)
+            dataVoList.add(it.toDataVo())
+        }
     }
 
     private fun dataSummary(
diff --git a/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt b/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
index cdcfcb9..5ae24c7 100644
--- a/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
+++ b/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
@@ -1,14 +1,15 @@
 package com.flightfeather.uav.common.utils
 
-import kotlin.math.PI
-import kotlin.math.asin
-import kotlin.math.cos
-import kotlin.math.sin
+import kotlin.math.*
 
 object MapUtil {
 
     private const val Ea = 6378137 //璧ら亾鍗婂緞
     private const val Eb = 6356725 //鏋佸崐寰�
+
+    // 鍧愭爣杞崲鍙傛暟
+    const val a = 6378245.0; //闀垮崐杞�
+    const val ee = 0.00669342162296594323; //鎵佺巼/*** GCJ02 杞崲涓� WGS84* @param lng* @param lat* @returns {*[]}*/
 
     /**
      * 鏍规嵁鍧愭爣鐐广�佽窛绂诲拰瑙掑害锛岃幏鍙栧彟涓�涓潗鏍�
@@ -93,6 +94,24 @@
     }
 
     /**
+     * 璁$畻澶氳竟褰㈢殑鍥涜嚦鑼冨洿
+     * @param polygon 澶氳竟褰㈠潗鏍囩偣鏁扮粍
+     * @return 鍥涜嚦鑼冨洿锛岄『搴忎负鏈�灏忕粡搴︼紝鏈�澶х粡搴�, 鏈�灏忕含搴︼紝鏈�澶х含搴�
+     */
+    fun calFourBoundaries(polygon: List<Pair<Double, Double>>): List<Double> {
+        // 璁$畻澶氳竟褰㈤《鐐圭粡搴﹁寖鍥村拰绾害鑼冨洿
+        val xsSort = polygon.map { it.first }.sorted()
+        val ysSort = polygon.map { it.second }.sorted()
+
+        val xMin = xsSort[0]
+        val yMin = ysSort[0]
+        val xMax = xsSort[xsSort.lastIndex]
+        val yMax = ysSort[ysSort.lastIndex]
+
+        return listOf(xMin, xMax, yMin, yMax)
+    }
+
+    /**
      * 鍒ゆ柇鍧愭爣鐐规槸鍚﹀湪澶氳竟褰㈢殑鍥涜嚦鑼冨洿鍐�
      * @param point 鍧愭爣鐐�
      * @param polygon 澶氳竟褰㈠潗鏍囩偣鏁扮粍
@@ -102,13 +121,19 @@
         val x = point.first
         val y = point.second
         // 璁$畻澶氳竟褰㈤《鐐圭粡搴﹁寖鍥村拰绾害鑼冨洿
-        val xsSort = polygon.map { it.first }.sorted()
-        val ysSort = polygon.map { it.second }.sorted()
+        val fb = calFourBoundaries(polygon)
+//        val xsSort = polygon.map { it.first }.sorted()
+//        val ysSort = polygon.map { it.second }.sorted()
+//
+//        val xMin = xsSort[0]
+//        val yMin = ysSort[0]
+//        val xMax = xsSort[xsSort.lastIndex]
+//        val yMax = ysSort[ysSort.lastIndex]
 
-        val xMin = xsSort[0]
-        val yMin = ysSort[0]
-        val xMax = xsSort[xsSort.lastIndex]
-        val yMax = ysSort[ysSort.lastIndex]
+        val xMin = fb[0]
+        val xMax = fb[1]
+        val yMin = fb[2]
+        val yMax = fb[3]
 
         return x >= xMin && x <= xMax && y >= yMin && y <= yMax
     }
@@ -146,7 +171,7 @@
      * @param point 鍧愭爣鐐�
      * @param polygon 澶氳竟褰㈠潗鏍囩偣鏁扮粍
      */
-    fun inPolygon(point: Pair<Double, Double>, polygon: List<Pair<Double, Double>>):Boolean {
+    fun inPolygon(point: Pair<Double, Double>, polygon: List<Pair<Double, Double>>): Boolean {
         val x = point.first
         val y = point.second
         var j = polygon.size - 1
@@ -177,4 +202,90 @@
         // 璁$畻鏄惁鍦ㄥ杈瑰舰鍐呴儴
         return inPolygon(point, polygon)
     }
+
+    /**
+     * 鍒ゆ柇缁忕含搴︽槸鍚﹀湪鍥藉唴
+     * @return true: 缁忕含搴︿笉鍦ㄥ浗鍐咃紝false锛氱粡绾害鍦ㄥ浗鍐�
+     */
+    fun outOfChina(point: Pair<Double, Double>): Boolean {
+        val lng = point.first
+        val lat = point.second
+        return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
+    }
+
+    private fun transformLat(point: Pair<Double, Double>): Double {
+        val lng = point.first
+        val lat = point.second
+        var ret = -100.0 +
+                2.0 * lng +
+                3.0 * lat +
+                0.2 * lat * lat +
+                0.1 * lng * lat +
+                0.2 * sqrt(abs(lng))
+        ret += ((20.0 * sin(6.0 * lng * PI) + 20.0 * sin(2.0 * lng * PI)) * 2.0) / 3.0
+        ret += ((20.0 * sin(lat * PI) + 40.0 * sin((lat / 3.0) * PI)) * 2.0) / 3.0
+        ret += ((160.0 * sin((lat / 12.0) * PI) + 320 * sin((lat * PI) / 30.0)) * 2.0) / 3.0
+        return ret;
+    }
+
+    private fun transformLng(point: Pair<Double, Double>): Double {
+        val lng = point.first
+        val lat = point.second
+        var ret = 300.0 +
+                lng +
+                2.0 * lat +
+                0.1 * lng * lng +
+                0.1 * lng * lat +
+                0.1 * sqrt(abs(lng));
+        ret += ((20.0 * sin(6.0 * lng * PI) + 20.0 * sin(2.0 * lng * PI)) * 2.0) / 3.0;
+        ret += ((20.0 * sin(lng * PI) + 40.0 * sin((lng / 3.0) * PI)) * 2.0) / 3.0;
+        ret += ((150.0 * sin((lng / 12.0) * PI) + 300.0 * sin((lng / 30.0) * PI)) * 2.0) / 3.0
+        return ret;
+    }
+
+    /**
+     * 鐏槦鍧愭爣绯昏浆WGS84鍧愭爣绯�
+     */
+    fun gcj02ToWgs84(point: Pair<Double, Double>): Pair<Double, Double> {
+        if (outOfChina(point)) {
+            return point;
+        } else {
+            val lng = point.first
+            val lat = point.second
+            var dlat = transformLat(lng - 105.0 to lat - 35.0);
+            var dlng = transformLng(lng - 105.0 to lat - 35.0);
+            val radlat = (lat / 180.0) * PI;
+            var magic = sin(radlat);
+            magic = 1 - ee * magic * magic;
+            val sqrtmagic = sqrt(magic);
+            dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
+            dlng = (dlng * 180.0) / ((a / sqrtmagic) * cos(radlat) * PI);
+            val mglat = Math.round((lat * 2 - lat - dlat) * 1000000) / 1000000;
+            val mglng = Math.round((lng * 2 - lng - dlng) * 1000000) / 1000000;
+            return mglng.toDouble() to mglat.toDouble()
+        }
+    }
+
+    /**
+     * WGS84鍧愭爣绯昏浆鐏槦鍧愭爣绯�
+     */
+    fun wgs84ToGcj02(point: Pair<Double, Double>): Pair<Double, Double> {
+        if (outOfChina(point)) {
+            return point
+        } else {
+            val lng = point.first
+            val lat = point.second
+            var dLat = transformLat(lng - 105.0 to lat - 35.0);
+            var dLon = transformLng(lng - 105.0 to lat - 35.0);
+            val radLat = (lat / 180.0) * PI;
+            var magic = sin(radLat);
+            magic = 1 - ee * magic * magic;
+            val sqrtMagic = sqrt(magic);
+            dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * PI);
+            dLon = (dLon * 180.0) / ((a / sqrtMagic) * cos(radLat) * PI);
+            val mgLat = lat + dLat;
+            val mgLon = lng + dLon;
+            return mgLon to mgLat
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/entity/SceneInfo.java b/src/main/kotlin/com/flightfeather/uav/domain/entity/SceneInfo.java
index 716dede..4b3ee4c 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/entity/SceneInfo.java
+++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/SceneInfo.java
@@ -28,9 +28,15 @@
     @Column(name = "Location")
     private String location;
 
+    /**
+     * 缁忓害锛岄珮寰峰湴鍥惧潗鏍囩郴
+     */
     @Column(name = "Longitude")
     private BigDecimal longitude;
 
+    /**
+     * 绾害锛岄珮寰峰湴鍥惧潗鏍囩郴
+     */
     @Column(name = "Latitude")
     private BigDecimal latitude;
 
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/repository/impl/AirDataRepImpl.kt b/src/main/kotlin/com/flightfeather/uav/domain/repository/impl/AirDataRepImpl.kt
index 5008d5b..2931e5b 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/repository/impl/AirDataRepImpl.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/repository/impl/AirDataRepImpl.kt
@@ -132,8 +132,7 @@
         dataList.forEach {vo ->
             when (UWDeviceType.getType(vo.deviceCode)) {
                 UWDeviceType.VEHICLE -> {
-                    val d = RealTimeDataVehicle()
-                    dataTransform(vo, d)
+                    val d = vo.toBaseRealTimeData(RealTimeDataVehicle::class.java)
                     /***************************************************************************************************/
                     // FIXME: 2021/10/27 杞﹁浇鐩戞祴閮ㄥ垎鍥犲瓙閲忕骇璋冩暣
                     calibration(d, UWDeviceType.VEHICLE)
@@ -142,8 +141,7 @@
                     res.add(d)
                 }
                 UWDeviceType.UAV -> {
-                    val d = RealTimeDataUav()
-                    dataTransform(vo, d)
+                    val d = vo.toBaseRealTimeData(RealTimeDataUav::class.java)
                     /***************************************************************************************************/
                     // FIXME: 2021/10/25 鏃犱汉鏈洪儴鍒嗗洜瀛愰噰鐢ㄨ溅杞芥暟鎹~鍏咃紝鍙栨渶鏂扮殑15鍒嗛挓鐨勬暟鎹�
                     if (tmpVehicleDataList.isEmpty()) {
@@ -167,8 +165,7 @@
                     res.add(d)
                 }
                 UWDeviceType.GRID -> {
-                    val d = RealTimeDataGrid()
-                    dataTransform(vo, d)
+                    val d = vo.toBaseRealTimeData(RealTimeDataGrid::class.java)
                     /**************************************************************************/
                     // FIXME: 2021/11/8 閽堝鍘嗗彶缃戞牸鍖栧師濮嬫暟鎹紝杩涜涓存椂鏍″噯澶勭悊
 //                    val dTime = LocalDateTime.ofInstant(d.dataTime?.toInstant(), ZoneId.systemDefault())
@@ -267,38 +264,6 @@
                     }
                 }
             }
-        }
-    }
-
-    fun dataTransform(vo: DataVo, bean: BaseRealTimeData) {
-        bean.apply {
-            deviceCode = vo.deviceCode
-            latitude = vo.lat?.toBigDecimal()
-            longitude = vo.lng?.toBigDecimal()
-            dataTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(vo.time)
-            createTime = Date()
-            vo.values?.forEach {
-                when (it.factorId?.toInt()) {
-                    FactorType.NO2.value -> no2 = it.factorData?.toFloat()
-                    FactorType.CO.value -> co = it.factorData?.toFloat()
-                    FactorType.H2S.value -> h2s = it.factorData?.toFloat()
-                    FactorType.SO2.value -> so2 = it.factorData?.toFloat()
-                    FactorType.O3.value -> o3 = it.factorData?.toFloat()
-
-                    FactorType.PM25.value -> pm25 = it.factorData?.toFloat()
-                    FactorType.PM10.value -> pm10 = it.factorData?.toFloat()
-                    FactorType.TEMPERATURE.value -> temperature = it.factorData?.toFloat()
-                    FactorType.HUMIDITY.value -> humidity = it.factorData?.toFloat()
-                    FactorType.VOC.value -> voc = it.factorData?.toFloat()
-
-                    FactorType.NOI.value -> noi = it.factorData?.toFloat()
-                    FactorType.VELOCITY.value -> velocity = it.factorData?.toFloat()
-                    FactorType.WIND_SPEED.value -> windSpeed = it.factorData?.toFloat()
-                    FactorType.WIND_DIRECTION.value -> windDirection = it.factorData?.toFloat()
-                    FactorType.HEIGHT.value -> height = it.factorData?.toFloat()
-                }
-            }
-
         }
     }
 
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt
index ab8b068..b548bc7 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt
@@ -1,9 +1,12 @@
 package com.flightfeather.uav.lightshare.bean
 
 import com.fasterxml.jackson.annotation.JsonInclude
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
 import com.flightfeather.uav.model.BaseMData
 import com.flightfeather.uav.socket.bean.AirData
 import com.flightfeather.uav.socket.eunm.FactorType
+import java.text.SimpleDateFormat
+import java.util.Date
 
 /**
  * @author riku
@@ -11,55 +14,86 @@
  */
 @JsonInclude(JsonInclude.Include.NON_NULL)
 data class DataVo(
-        //鏃堕棿,  yyyy-MM-dd HH:mm:ss
-        var time: String? = null,
-        //绔欑偣缂栧彿
-        var deviceCode: String? = null,
-        //鏁版嵁鍊�
-        var values: List<AirData>? = null,
-        //缁忓害
-        var lng: Double? = null,
-        //绾害
-        var lat: Double? = null
+    //鏃堕棿,  yyyy-MM-dd HH:mm:ss
+    var time: String? = null,
+    //绔欑偣缂栧彿
+    var deviceCode: String? = null,
+    //鏁版嵁鍊�
+    var values: List<AirData>? = null,
+    //缁忓害
+    var lng: Double? = null,
+    //绾害
+    var lat: Double? = null,
 ) : BaseMData() {
-        override fun getFactorData(type: FactorType): Double? {
-                if (values == null) throw IllegalStateException(this.javaClass.name + ": 鐩戞祴鏁版嵁鏁扮粍涓簄ull")
-                for (d in values!!) {
-                        if (d.factorName == type.name) {
-                                return d.factorData
-                        }
-                }
-                return null
+    override fun getFactorData(type: FactorType): Double? {
+        if (values == null) throw IllegalStateException(this.javaClass.name + ": 鐩戞祴鏁版嵁鏁扮粍涓簄ull")
+        for (d in values!!) {
+            if (d.factorName == type.name) {
+                return d.factorData
+            }
         }
+        return null
+    }
 
-        fun toRowContent(): Array<Any> {
-                val row = mutableListOf<Any>()
-                row.add(deviceCode ?: "")
-                row.add(time ?: "")
-                row.add(lng ?: -1.0)
-                row.add(lat ?: -1.0)
-                values?.forEach {
-                        if (FactorType.outputFactor(it.factorName)) {
-                                row.add(it.factorData ?: -1.0)
+    fun toRowContent(): Array<Any> {
+        val row = mutableListOf<Any>()
+        row.add(deviceCode ?: "")
+        row.add(time ?: "")
+        row.add(lng ?: -1.0)
+        row.add(lat ?: -1.0)
+        values?.forEach {
+            if (FactorType.outputFactor(it.factorName)) {
+                row.add(it.factorData ?: -1.0)
 //                                row.add(it.physicalQuantity ?: -1.0)
-                        }
-                }
-                return row.toTypedArray()
+            }
         }
+        return row.toTypedArray()
+    }
 
-        fun toRowTitle(): Array<String> {
-                val list = mutableListOf<String>()
-                list.add("缂栧彿")
-                list.add("閲囨牱鏃堕棿")
-                list.add("缁忓害")
-                list.add("绾害")
-                values?.forEach {
-                        if (FactorType.outputFactor(it.factorName)) {
-                                val name = it.factorName ?: ""
-                                list.add(name)
+    fun toRowTitle(): Array<String> {
+        val list = mutableListOf<String>()
+        list.add("缂栧彿")
+        list.add("閲囨牱鏃堕棿")
+        list.add("缁忓害")
+        list.add("绾害")
+        values?.forEach {
+            if (FactorType.outputFactor(it.factorName)) {
+                val name = it.factorName ?: ""
+                list.add(name)
 //                        list.add("$name(鐗╃悊閲�)")
-                        }
-                }
-                return list.toTypedArray()
+            }
         }
+        return list.toTypedArray()
+    }
+
+    fun <T : BaseRealTimeData> toBaseRealTimeData(clz:Class<T>): T {
+        return clz.newInstance().apply {
+            deviceCode = this@DataVo.deviceCode
+            latitude = this@DataVo.lat?.toBigDecimal()
+            longitude = this@DataVo.lng?.toBigDecimal()
+            dataTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this@DataVo.time)
+            createTime = Date()
+            this@DataVo.values?.forEach {
+                when (it.factorId?.toInt()) {
+                    FactorType.NO2.value -> no2 = it.factorData?.toFloat()
+                    FactorType.CO.value -> co = it.factorData?.toFloat()
+                    FactorType.H2S.value -> h2s = it.factorData?.toFloat()
+                    FactorType.SO2.value -> so2 = it.factorData?.toFloat()
+                    FactorType.O3.value -> o3 = it.factorData?.toFloat()
+
+                    FactorType.PM25.value -> pm25 = it.factorData?.toFloat()
+                    FactorType.PM10.value -> pm10 = it.factorData?.toFloat()
+                    FactorType.TEMPERATURE.value -> temperature = it.factorData?.toFloat()
+                    FactorType.HUMIDITY.value -> humidity = it.factorData?.toFloat()
+                    FactorType.VOC.value -> voc = it.factorData?.toFloat()
+
+                    FactorType.NOI.value -> noi = it.factorData?.toFloat()
+                    FactorType.VELOCITY.value -> velocity = it.factorData?.toFloat()
+                    FactorType.WIND_SPEED.value -> windSpeed = it.factorData?.toFloat()
+                    FactorType.WIND_DIRECTION.value -> windDirection = it.factorData?.toFloat()
+                    FactorType.HEIGHT.value -> height = it.factorData?.toFloat()
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/ExceptionStatusType.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/ExceptionStatusType.kt
new file mode 100644
index 0000000..d83afaa
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/ExceptionStatusType.kt
@@ -0,0 +1,11 @@
+package com.flightfeather.uav.lightshare.eunm
+
+/**
+ * 寮傚父鐘舵��
+ * @date 2025/5/14
+ * @author feiyu02
+ */
+enum class ExceptionStatusType(val value: Int, val des: String) {
+    InProgress(1, "鎸佺画涓�"),
+    Ended(2, "宸茬粨鏉�"),
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt b/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt
index 1abff63..09adc8b 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt
@@ -1,5 +1,6 @@
 package com.flightfeather.uav.socket
 
+import com.flightfeather.uav.domain.repository.SceneInfoRep
 import com.flightfeather.uav.socket.handler.ServerHandler
 import com.flightfeather.uav.socket.handler.UnderwayWebSocketServerHandler
 import com.flightfeather.uav.socket.processor.BaseProcessor
@@ -25,7 +26,7 @@
  * *******************************************************************************/
 @Component
 class UnderwaySocketServer(
-    private val underwayWebSocketServerHandler:UnderwayWebSocketServerHandler
+    private val sceneInfoRep: SceneInfoRep
 ) {
 
     private val bossGroup = NioEventLoopGroup()
@@ -39,8 +40,8 @@
         electricServer(processor)?.bind(port)?.sync()
     }
 
-    fun startWebSocketServer(port: Int, processor: BaseProcessor) {
-        webSocketServer(processor)?.bind(port)?.sync()
+    fun startWebSocketServer(port: Int) {
+        webSocketServer()?.bind(port)?.sync()
     }
 
     fun stopServer() {
@@ -92,13 +93,13 @@
     /**
      * 澶氬弬鏁拌蛋鑸湇鍔$
      */
-    private fun webSocketServer(processor: BaseProcessor):ServerBootstrap? = newServer(object : ChannelInitializer<NioSocketChannel>() {
+    private fun webSocketServer():ServerBootstrap? = newServer(object : ChannelInitializer<NioSocketChannel>() {
         override fun initChannel(p0: NioSocketChannel?) {
             p0?.pipeline()
                 ?.addLast(HttpServerCodec())
                 ?.addLast(HttpObjectAggregator(65535))
                 ?.addLast(WebSocketServerProtocolHandler("/ws"))
-                ?.addLast(underwayWebSocketServerHandler)
+                ?.addLast(UnderwayWebSocketServerHandler(sceneInfoRep))
         }
     })
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/handler/BaseHandler.kt b/src/main/kotlin/com/flightfeather/uav/socket/handler/BaseHandler.kt
index 90808a1..42634b7 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/handler/BaseHandler.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/handler/BaseHandler.kt
@@ -15,12 +15,10 @@
     abstract var tag: String
 
     override fun channelRegistered(ctx: ChannelHandlerContext?) {
-        super.channelRegistered(ctx)
         println("------銆�${tag}銆慖P杩炴帴锛歔ip:${ctx?.channel()?.remoteAddress()}] ${
             SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
                 Date()
             )}")
-//        ctx?.fireChannelActive()
     }
 
     override fun channelActive(ctx: ChannelHandlerContext?) {
@@ -28,19 +26,13 @@
             SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
                 Date()
             )}")
-        super.channelActive(ctx)
     }
 
     override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
-        super.channelRead(ctx, msg)
         println("------銆�${tag}銆戞敹鍒扮殑鍘熷鏁版嵁锛歔ip:${ctx?.channel()?.remoteAddress()}] ${
             SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
                 Date()
             )}")
-    }
-
-    override fun channelReadComplete(ctx: ChannelHandlerContext?) {
-        super.channelReadComplete(ctx)
     }
 
     override fun channelInactive(ctx: ChannelHandlerContext?) {
@@ -48,7 +40,6 @@
             SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
                 Date()
             )}")
-        super.channelInactive(ctx)
     }
 
     @Deprecated("Deprecated in Java")
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt b/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt
index 0e912d3..3c5b0fa 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt
@@ -1,5 +1,13 @@
 package com.flightfeather.uav.socket.handler
 
+import com.flightfeather.uav.biz.sourcetrace.RealTimeExceptionAnalysisController
+import com.flightfeather.uav.common.api2word.utils.JsonUtils
+import com.flightfeather.uav.common.utils.GsonUtils
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.repository.SceneInfoRep
+import com.flightfeather.uav.lightshare.bean.DataVo
+import com.flightfeather.uav.socket.sender.UnderwayWebSocketSender
+import com.google.gson.JsonSyntaxException
 import io.netty.channel.ChannelHandlerContext
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame
 import org.springframework.stereotype.Component
@@ -9,28 +17,39 @@
  * @date 2025/5/13
  * @author feiyu02
  */
-@Component
-class UnderwayWebSocketServerHandler : BaseHandler() {
+class UnderwayWebSocketServerHandler(sceneInfoRep: SceneInfoRep) : BaseHandler() {
 
-    private val sessionPool = mutableMapOf<String?, ChannelHandlerContext?>()
+    private val realTimeExceptionAnalysisController = RealTimeExceptionAnalysisController(sceneInfoRep)
 
     override var tag: String = "UAV-WS"
 
     override fun channelRegistered(ctx: ChannelHandlerContext?) {
         super.channelRegistered(ctx)
         // 灏嗚繛鎺ュ瓨鍌�
-        if (!sessionPool.containsKey(ctx?.name())) {
-            sessionPool[ctx?.name()] = ctx
-        }
+        UnderwayWebSocketSender.saveSession(ctx)
     }
 
     override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
         super.channelRead(ctx, msg)
-
         when (msg) {
-            is TextWebSocketFrame->{
-                println(msg.text())
-                ctx?.channel()?.writeAndFlush(msg)
+            is TextWebSocketFrame -> {
+                val msgTxt = msg.text()
+
+                println(msgTxt)
+//                ctx?.channel()?.writeAndFlush(msg)
+
+                // Test
+                try {
+                    val data = GsonUtils.parserJsonToArrayBeans(msgTxt, DataVo::class.java)
+                    data.forEach {
+                        realTimeExceptionAnalysisController.addOneData(
+                            it.toBaseRealTimeData(BaseRealTimeData::class.java)
+                        )
+                    }
+                } catch (e: Exception) {
+//                    ctx?.channel()?.writeAndFlush(TextWebSocketFrame("褰撳墠涓烘祴璇曠姸鎬侊紝浼犺緭鐨勬暟鎹笉鏄蛋鑸暟鎹牸寮�"))
+                    println("褰撳墠涓烘祴璇曠姸鎬侊紝浼犺緭鐨勬暟鎹笉鏄蛋鑸暟鎹牸寮�")
+                }
             }
         }
     }
@@ -38,18 +57,6 @@
     override fun channelInactive(ctx: ChannelHandlerContext?) {
         super.channelInactive(ctx)
         // 灏嗚繛鎺ョЩ闄�
-        if (sessionPool.containsKey(ctx?.name())) {
-            sessionPool.remove(ctx?.name())
-        }
-    }
-
-    fun send() {
-
-    }
-
-    fun broadcast(msg: String) {
-        sessionPool.forEach { t, u ->
-            u?.channel()?.writeAndFlush(TextWebSocketFrame(msg))
-        }
+        UnderwayWebSocketSender.removeSession(ctx)
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt b/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt
index a649598..6c5f374 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt
@@ -7,6 +7,7 @@
 import com.flightfeather.uav.model.epw.EPWDataPrep
 import com.flightfeather.uav.domain.repository.AirDataRep
 import com.flightfeather.uav.domain.repository.RealTimeDataRep
+import com.flightfeather.uav.domain.repository.SceneInfoRep
 import com.flightfeather.uav.domain.repository.SegmentInfoRep
 import com.flightfeather.uav.socket.bean.AirDataPackage
 import com.flightfeather.uav.socket.decoder.AirDataDecoder
@@ -31,10 +32,7 @@
 @Component
 class UnderwayProcessor(
     private val airDataRep: AirDataRep,
-    private val realTimeDataRep: RealTimeDataRep,
-    private val locationRoadNearby: LocationRoadNearby,
-    private val segmentInfoRep: SegmentInfoRep,
-    private val underwayWebSocketServerHandler: UnderwayWebSocketServerHandler,
+    private val sceneInfoRep: SceneInfoRep,
 ) : BaseProcessor() {
 
     companion object {
@@ -48,23 +46,7 @@
     private val dataProcessMap = mutableMapOf<String?, EPWDataPrep>()
 
     // 瀹炴椂璧拌埅姹℃煋婧簮澶勭悊鍣�
-    private val realTimeExceptionAnalysisController =
-        RealTimeExceptionAnalysisController(
-            realTimeDataRep,
-            locationRoadNearby,
-            segmentInfoRep,
-            underwayWebSocketServerHandler,
-            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()
-        )
+    private val realTimeExceptionAnalysisMap = mutableMapOf<String?, RealTimeExceptionAnalysisController>()
 
     override var tag: String = "璧拌埅鐩戞祴"
 
@@ -77,8 +59,12 @@
             deviceSession.saveDevice(packageData.deviceCode, ctx)
             saveToTxt(msg)
             saveToDataBase(packageData)?.takeIf { it.isNotEmpty() }?.get(0)?.let {
+                // 姣忓彴璁惧鏈夊悇鑷崟鐙殑寮傚父鏁版嵁澶勭悊鍣�
+                if (!realTimeExceptionAnalysisMap.containsKey(it.deviceCode)) {
+                    realTimeExceptionAnalysisMap[it.deviceCode] = RealTimeExceptionAnalysisController(sceneInfoRep)
+                }
                 // 灏嗚蛋鑸暟鎹紶鍏ュ紓甯稿鐞嗗櫒
-                realTimeExceptionAnalysisController.addOneData(it)
+                realTimeExceptionAnalysisMap[it.deviceCode]?.addOneData(it)
             }
 
         } else {
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt b/src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt
new file mode 100644
index 0000000..2a765fe
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt
@@ -0,0 +1,36 @@
+package com.flightfeather.uav.socket.sender
+
+import io.netty.channel.ChannelHandlerContext
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame
+
+/**
+ *
+ * @date 2025/5/14
+ * @author feiyu02
+ */
+object UnderwayWebSocketSender {
+
+    private val sessionPool = mutableMapOf<String?, ChannelHandlerContext?>()
+
+    fun saveSession(ctx: ChannelHandlerContext?) {
+        if (!sessionPool.containsKey(ctx?.name())) {
+            sessionPool[ctx?.name()] = ctx
+        }
+    }
+
+    fun removeSession(ctx: ChannelHandlerContext?) {
+        if (sessionPool.containsKey(ctx?.name())) {
+            sessionPool.remove(ctx?.name())
+        }
+    }
+
+    fun send() {
+
+    }
+
+    fun broadcast(msg: String) {
+        sessionPool.forEach { (t, u) ->
+            u?.channel()?.writeAndFlush(TextWebSocketFrame(msg))
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImplTest.kt b/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImplTest.kt
index 1d70ec0..2865f6c 100644
--- a/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImplTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImplTest.kt
@@ -23,8 +23,6 @@
     @Autowired
     lateinit var realTimeDataVehicleMapper: RealTimeDataVehicleMapper
 
-    @Autowired
-    lateinit var airDataRepository: AirDataRepImpl
 
     @Test
     fun outToExcel() {
@@ -153,8 +151,7 @@
             }
             println("褰撳墠椤垫暟锛�$page")
             res.data?.forEach {vo ->
-                val d = RealTimeDataVehicle()
-                airDataRepository.dataTransform(vo, d)
+                val d = vo.toBaseRealTimeData(RealTimeDataVehicle::class.java)
                 realTimeDataVehicleMapper.insert(d)
                 count++
             }
diff --git a/src/test/kotlin/com/flightfeather/uav/socket/UnderwayProcessorTest.kt b/src/test/kotlin/com/flightfeather/uav/socket/UnderwayProcessorTest.kt
index cfa3b78..8c62914 100644
--- a/src/test/kotlin/com/flightfeather/uav/socket/UnderwayProcessorTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/socket/UnderwayProcessorTest.kt
@@ -2,14 +2,21 @@
 
 import com.flightfeather.uav.socket.processor.UnderwayProcessor
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.junit4.SpringRunner
 
 /**
  * @author riku
  * Date: 2019/9/16
  */
+@RunWith(SpringRunner::class)
+@SpringBootTest
 class UnderwayProcessorTest {
 
-    private val messageManager = UnderwayProcessor()
+    @Autowired
+    lateinit var messageManager: UnderwayProcessor
 
     @Test
     fun bccCheck() {

--
Gitblit v1.9.3