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

---
 src/main/kotlin/com/flightfeather/uav/socket/handler/BaseHandler.kt                                   |   59 +++
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt                    |   10 
 src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt                                  |   28 +
 src/main/kotlin/com/flightfeather/uav/socket/handler/ServerHandler.kt                                 |    3 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionContinuous.kt    |   43 ++
 src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt                                         |    3 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt          |   71 +++
 src/main/kotlin/com/flightfeather/uav/common/utils/LocalDateTimeAdapter.java                          |   30 +
 src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt                           |   61 ++
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt         |   20 
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt           |   17 
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionResult.kt                       |   65 +++
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt               |   33 -
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionAnalysis.kt                       |   80 ----
 src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt                               |    3 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt                |  105 +++++
 src/main/kotlin/com/flightfeather/uav/domain/repository/AirDataRep.kt                                 |    3 
 src/main/kotlin/com/flightfeather/uav/common/utils/GsonUtils.kt                                       |    7 
 src/main/kotlin/com/flightfeather/uav/UAVApplication.kt                                               |    8 
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt        |    9 
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionResult.kt                         |    9 
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt                     |  126 +++---
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt                 |    2 
 /dev/null                                                                                             |   32 -
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt                       |   30 +
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt |   92 +++++
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt                          |   14 
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt           |   26 +
 src/main/kotlin/com/flightfeather/uav/domain/repository/impl/AirDataRepImpl.kt                        |   12 
 src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt                |   55 +++
 src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt              |    2 
 31 files changed, 815 insertions(+), 243 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt b/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt
index f9023b1..c660050 100644
--- a/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt
+++ b/src/main/kotlin/com/flightfeather/uav/UAVApplication.kt
@@ -18,10 +18,14 @@
     @Autowired
     lateinit var electricProcessor: ElectricProcessor
 
+    @Autowired
+    lateinit var underwaySocketServer:UnderwaySocketServer
+
     @Bean
     fun runner() = ApplicationRunner{
-        UnderwaySocketServer().startUnderwayServer(9030, underwayProcessor)
-        UnderwaySocketServer().startElectricServer(9009, electricProcessor)
+        underwaySocketServer.startWebSocketServer(9031, underwayProcessor)
+        underwaySocketServer.startUnderwayServer(9030, underwayProcessor)
+        underwaySocketServer.startElectricServer(9009, electricProcessor)
     }
 }
 
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt
new file mode 100644
index 0000000..c3c1992
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt
@@ -0,0 +1,14 @@
+package com.flightfeather.uav.biz.dataanalysis
+
+import com.flightfeather.uav.biz.FactorFilter
+
+/**
+ * 鏁版嵁鍒嗘瀽閰嶇疆鍙傛暟鍩虹被
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+abstract class BaseAnalysisConfig(
+    // 鍥犲瓙绛涢��
+    val factorFilter: FactorFilter,
+) {
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionAnalysis.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionAnalysis.kt
index 4023fec..54e05f4 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionAnalysis.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionAnalysis.kt
@@ -1,93 +1,17 @@
 package com.flightfeather.uav.biz.dataanalysis
 
 import com.flightfeather.uav.biz.FactorFilter
-import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
-import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
-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.socket.eunm.FactorType
-import java.time.Duration
 
 /**
  * 鐩戞祴鏁版嵁寮傚父鍒嗘瀽鍩虹被
  */
-abstract class BaseExceptionAnalysis(config: DataAnalysisConfig) :
-    BaseDataAnalysis<BaseRealTimeData, DataAnalysisConfig, ExceptionResult>(config) {
+abstract class BaseExceptionAnalysis<V : BaseAnalysisConfig, Y : BaseExceptionResult>(config: V) :
+    BaseDataAnalysis<BaseRealTimeData, V, Y>(config) {
 
     /**
      * 纭畾寮傚父绫诲瀷
      */
     abstract fun getExceptionType(): ExceptionType
-
-    /**
-     * 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画
-     */
-    open fun isContinuous(d1: BaseRealTimeData?, d2: BaseRealTimeData?): Boolean {
-        if (d1 == null || d2 == null) return true
-
-        val t1 = d1.dataTime
-        val t2 = d2.dataTime
-        return Duration.between(t1?.toInstant(), t2?.toInstant()).toMillis() <= (20 * 1000)
-    }
-
-    /**
-     * 鐢熸垚涓�鏉″紓甯稿垎鏋愮粨鏋�
-     */
-    open fun newResult(
-        start: BaseRealTimeData, end: BaseRealTimeData?, factor: FactorFilter.SelectedFactor,
-        exceptionData: List<BaseRealTimeData>,
-    ): ExceptionResult {
-        val eType = getExceptionType()
-        return ExceptionResult().apply {
-            missionCode = config.mission.missionCode
-            deviceCode = start.deviceCode
-            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
-            startDate = start.dataTime
-            endDate = end?.dataTime
-            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
-
-            val avgData = exceptionData.avg()
-            // 姹傚彇姹℃煋鏁版嵁鐨勪腑蹇冨潗鏍�
-            longitude = avgData.longitude
-            latitude = avgData.latitude
-            // 姹傚彇涓绘薄鏌撳洜瀛愮殑鍧囧�煎拰鑼冨洿
-            val s = dataSummary(exceptionData, factor.main)
-            avg = s.first
-            min = s.second
-            max = s.third
-
-            exceptionData.forEach { dataList.add(it) }
-        }
-    }
-
-    private fun dataSummary(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Triple<Float, Float, Float> {
-        var min = -1f
-        var max = -1f
-        var total = 0f
-        var count = 0
-        exceptionData.forEach {
-            val value = it?.getByFactorType(factorType) ?: return@forEach
-            if (min == -1f || min > value) {
-                min = value
-            }
-            if (max == -1f || max < value) {
-                max = value
-            }
-            total += value
-            count++
-        }
-        val avg = if (count == 0) 0f else total / count
-        return Triple(avg, min, max)
-    }
 }
\ No newline at end of file
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 63cd9af..52f9cac 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -1,14 +1,15 @@
 package com.flightfeather.uav.biz.dataanalysis
 
 import com.flightfeather.uav.biz.FactorFilter
-import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
 import com.flightfeather.uav.socket.eunm.FactorType
+import java.time.Duration
 
 /**
  * 杩炵画绫诲瀷鐨勫紓甯稿垎鏋愬熀绫�,閫傜敤浜庡綋鍓嶆暟鎹笌鐩搁偦鏁版嵁涔嬮棿鏈夊叧鑱斿叧绯荤殑鎯呭喌
  */
-abstract class BaseExceptionContinuous(config: DataAnalysisConfig) : BaseExceptionAnalysis(config) {
+abstract class BaseExceptionContinuous<V : BaseAnalysisConfig, Y : BaseExceptionResult>(config: V) :
+    BaseExceptionAnalysis<V, Y>(config) {
 
     companion object {
         // 璁板綍寮傚父鏁版嵁娈垫椂锛屽垎鍒悜璧峰鍓嶅拰鏈熬鍚庨澶栬褰曠殑鏁版嵁涓暟鍋忕Щ閲�
@@ -20,7 +21,7 @@
         var sIndex = 0
 
         // 璧峰鏁版嵁瀵硅薄
-        var startData :BaseRealTimeData? = null
+        var startData: BaseRealTimeData? = null
 
         // 鏈熬鏁版嵁涓嬫爣
         var eIndex = -1
@@ -38,30 +39,28 @@
             sIndex = eIndex
             startData = data
             exceptionData.clear()
-            exceptionData.add(data)
+//            exceptionData.add(data)
         }
     }
 
     protected val tagMap = mutableMapOf<FactorType, Tag>()
 
-//    // 璧峰鏁版嵁涓嬫爣
-//    protected var sIndex = mutableListOf<Int>()
-//
-//    // 璧峰鏁版嵁瀵硅薄
-//    protected var startData = mutableListOf<BaseRealTimeData?>()
-//
-//    // 鏈熬鏁版嵁涓嬫爣
-//    protected var eIndex = mutableListOf<Int>()
-//
-//    // 寮傚父鏁版嵁娈�
-//    protected var exceptionData = mutableListOf<MutableList<BaseRealTimeData>>()
-
-//    protected var existException = mutableListOf<Boolean>()
-
     // 璧峰鏁版嵁涓庢湯灏炬暟鎹棿闅�
     open var durationCount = 1
+
     // 鏈熬鏁版嵁瀵硅薄
     protected var lastData: BaseRealTimeData? = null
+
+    /**
+     * 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画
+     */
+    open fun isContinuous(d1: BaseRealTimeData?, d2: BaseRealTimeData?): Boolean {
+        if (d1 == null || d2 == null) return true
+
+        val t1 = d1.dataTime
+        val t2 = d2.dataTime
+        return Duration.between(t1?.toInstant(), t2?.toInstant()).toMillis() <= (20 * 1000)
+    }
 
     /**
      * 鍒ゆ柇鏄惁婊¤冻寮傚父鏉′欢
@@ -69,24 +68,25 @@
     abstract fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean>
 
     /**
-     * 鍒ゆ柇寮傚父鍑虹幇鐨勮繛缁椂闀挎槸鍚︽弧瓒虫潯浠�
-     * @param sIndex
-     * @param eIndex
+     * 鍒ゆ柇寮傚父鍑虹幇鐨勮繛缁釜鏁版槸鍚︽弧瓒虫潯浠�
+     * @param tag 寮傚父鏁版嵁瀵硅薄
      */
-    abstract fun judgeDuration(sIndex: Int, eIndex: Int): Boolean
+    abstract fun judgeExceptionCount(tag: Tag): Boolean
+
+    /**
+     * 寮傚父鏁版嵁鐨勬埅鍙栧垽鏂�
+     * 鏄惁闇�瑕侀檺鍒朵竴缁勫紓甯告暟鎹殑闀垮害
+     * @return 榛樿涓嶉渶瑕佹埅鍙�
+     */
+    open fun needCut(tag: Tag): Boolean {
+        return false
+    }
 
     override fun init() {
         super.init()
         lastData = null
-//        repeat(config.factorCount) {
-//            startData.add(null)
-//            sIndex.add(0)
-//            eIndex.add(-1)
-//            existException.add(false)
-//            exceptionData.add(mutableListOf())
-//        }
         tagMap.clear()
-        config.factorFilter.mainList().forEach {f->
+        config.factorFilter.mainList().forEach { f ->
             tagMap[f] = Tag()
         }
     }
@@ -94,17 +94,17 @@
     override fun onNextData(data: BaseRealTimeData) {
         val isContinue = isContinuous(lastData, data)
         val hasException = judgeException(lastData, data)
-        config.factorFilter.selectedList.forEach {s->
+        config.factorFilter.selectedList.forEach { s ->
             val f = s.main
             tagMap[f]?.let {
                 it.eIndex++
                 // 璧峰鏁版嵁
-                it.endData = lastData
-                if (it.endData == null) {
+                it.endData = data
+                if (it.startData == null) {
                     it.refreshAfterCheckResult(data)
                 }
                 // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇
-                if (!isContinue) {
+                if (!isContinue || needCut(it)) {
                     checkResult(s)
                     // 鏁版嵁涓嶈繛缁椂锛岃褰曞紓甯告儏鍐�
                     if (it.eIndex - it.sIndex >= durationCount) {
@@ -131,43 +131,51 @@
         checkResult()
     }
 
-//    fun refreshAfterCheckResult(i:Int, data: BaseRealTimeData) {
-//        sIndex[i] = eIndex[i]
-//        startData[i] = data
-//        exceptionData[i].clear()
-//        exceptionData[i].add(data)
-//    }
-
     /**
      * 妫�鏌ヨ繛缁紓甯哥粨鏉熸椂锛屾槸鍚︾鍚堝紓甯稿瓨鍌ㄦ潯浠�
      */
     open fun checkResult(factor: FactorFilter.SelectedFactor? = null) {
         val tag = tagMap[factor?.main]
         if (factor != null && tag != null) {
-            if (tag.existException && judgeDuration(tag.sIndex, tag.eIndex - 1)) {
-                tag.startData?.let {
-                    resultList.add(newResult(it, lastData, factor, tag.exceptionData))
-                }
-                tag.existException = false
+            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 && judgeDuration(tag1.sIndex, tag1.eIndex - 1)) {
-                    tag1.startData?.let {
-                        resultList.add(newResult(it, lastData, f, tag1.exceptionData))
-                    }
-                    tag1.existException = false
+                if (tag1.existException && judgeExceptionCount(tag1)) {
+                    onNewException(tag1, f)
+//                    tag1.startData?.let {
+//                        resultList.add(newResult(it, lastData, f, tag1.exceptionData))
+//                    }
+//                    tag1.existException = false
                 }
             }
-//            repeat(config.factorCount) { i ->
-//                if (existException[i] && judgeDuration(sIndex[i], eIndex[i])) {
-//                    startData[i]?.let {
-//                        resultList.add(newResult(it, lastData, i, exceptionData[i]))
-//                    }
-//                    existException[i] = false
-//                }
-//            }
         }
     }
+
+    /**
+     * 鏂板涓�鏉″紓甯�
+     */
+    open fun onNewException(tag:Tag, factor: FactorFilter.SelectedFactor) {
+        tag.startData?.let {
+            resultList.add(newResult(it, lastData, factor, tag.exceptionData))
+        }
+        tag.existException = false
+    }
+
+    /**
+     * 鐢熸垚涓�鏉″紓甯稿垎鏋愮粨鏋�
+     */
+    abstract fun newResult(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+    ): Y
+
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt
index 9a10bdc..3912148 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt
@@ -1,17 +1,17 @@
 package com.flightfeather.uav.biz.dataanalysis
 
-import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
 
 /**
  * 杩炵画绫诲瀷鐨勫紓甯稿垎鏋愬熀绫�,鍖哄埆浜庣埗绫荤殑鍦版柟鍦ㄤ簬姝ょ寮傚父鍙拰鍗曚釜鏁版嵁鏈韩鏈夊叧,涓庣浉閭绘暟鎹棤鍏�
  */
-abstract class BaseExceptionContinuousSingle(config: DataAnalysisConfig) : BaseExceptionContinuous(config) {
+abstract class BaseExceptionContinuousSingle<V : BaseAnalysisConfig, Y : BaseExceptionResult>(config: V) :
+    BaseExceptionContinuous<V, Y>(config) {
 
     override fun onNextData(data: BaseRealTimeData) {
         val isContinue = isContinuous(lastData, data)
         val hasException = judgeException(lastData, data)
-        config.factorFilter.selectedList.forEach {s->
+        config.factorFilter.selectedList.forEach { s ->
             val f = s.main
             tagMap[f]?.let {
                 it.eIndex++
@@ -19,12 +19,12 @@
                     it.startData = data
                 }
                 // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇
-                if (!isContinue) {
+                if (!isContinue || needCut(it)) {
                     checkResult(s)
                     it.refreshAfterCheckResult(data)
                 } else {
                     if (hasException[f] == true) {
-                        // 淇敼浜嗚捣濮嬫暟鎹殑浣嶇疆,鍙樻洿涓哄嚭鐜板紓甯哥殑璇ュ��,鑰屼笉鏄師鏉ョ殑鍑虹幇寮傚父鐨勬暟鎹殑鍓嶄竴涓��
+                        // 淇敼浜嗚捣濮嬫暟鎹殑浣嶇疆,鍙樻洿涓哄嚭鐜板紓甯哥殑璇ュ��,鑰屼笉鏄師鏉ョ殑鍑虹幇寮傚父鏁版嵁鐨勫墠涓�涓��
                         if (!it.existException) {
                             it.sIndex = it.eIndex
                             it.startData = data
@@ -38,29 +38,6 @@
                 }
             }
         }
-//        repeat(config.factorCount) { i ->
-//            eIndex[i]++
-//            if (lastData == null) {
-//                startData[i] = data
-//            }
-//            // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇
-//            if (!isContinue) {
-//                checkResult()
-//                sIndex[i] = eIndex[i]
-//                startData[i] = data
-//            } else {
-//                if (hasException[i]) {
-//                    // 淇敼浜嗚捣濮嬫暟鎹殑浣嶇疆,鍙樻洿涓哄嚭鐜板紓甯哥殑璇ュ��,鑰屼笉鏄師鏉ョ殑鍑虹幇寮傚父鐨勬暟鎹殑鍓嶄竴涓��
-//                    if (!existException[i]) {
-//                        sIndex[i] = eIndex[i]
-//                        startData[i] = data
-//                    }
-//                    existException[i] = true
-//                } else {
-//                    checkResult()
-//                }
-//            }
-//        }
         lastData = data
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionResult.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionResult.kt
new file mode 100644
index 0000000..3500ba0
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionResult.kt
@@ -0,0 +1,9 @@
+package com.flightfeather.uav.biz.dataanalysis
+
+/**
+ * 寮傚父缁撴灉鍩虹被
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+abstract class BaseExceptionResult {
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt
index d2f3c57..556353a 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt
@@ -25,7 +25,7 @@
 
     var running = false
 
-    private val taskList = mutableListOf<BaseExceptionAnalysis>()
+    private val taskList = mutableListOf<BaseExceptionAnalysis<DataAnalysisConfig, ExceptionResult>>()
 
     private fun initTask(config: DataAnalysisConfig) {
         taskList.clear()
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/RealTimeExceptionAnalysisController.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/RealTimeExceptionAnalysisController.kt
deleted file mode 100644
index 91bd310..0000000
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/RealTimeExceptionAnalysisController.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.flightfeather.uav.biz.dataanalysis
-
-import com.flightfeather.uav.biz.dataanalysis.exceptiontype.ExceptionSlideAverage
-import com.flightfeather.uav.biz.dataanalysis.exceptiontype.ExceptionValueMutation
-import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
-import com.flightfeather.uav.common.location.LocationRoadNearby
-import com.flightfeather.uav.domain.repository.RealTimeDataRep
-import com.flightfeather.uav.domain.repository.SegmentInfoRep
-
-/**
- *
- * @date 2025/5/8
- * @author feiyu02
- */
-class RealTimeExceptionAnalysisController (
-    private val realTimeDataRep: RealTimeDataRep,
-    private val locationRoadNearby: LocationRoadNearby,
-    private val segmentInfoRep: SegmentInfoRep,
-){
-
-    private val taskList = mutableListOf<BaseExceptionAnalysis>()
-
-    private fun initTask(config: DataAnalysisConfig) {
-        taskList.clear()
-        taskList.apply {
-            add(ExceptionValueMutation(config))
-            add(ExceptionSlideAverage(config))
-        }
-    }
-
-    // 璁$畻鍘嗗彶浠诲姟
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt
new file mode 100644
index 0000000..3af5a17
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt
@@ -0,0 +1,26 @@
+package com.flightfeather.uav.biz.dataanalysis.exceptiontype
+
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuous
+import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+
+/**
+ * 杩炵画绫诲瀷鐨勫紓甯稿垎鏋愬熀绫�,閫傜敤浜庡綋鍓嶆暟鎹笌鐩搁偦鏁版嵁涔嬮棿鏈夊叧鑱斿叧绯荤殑鎯呭喌
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+abstract class ExceptionContinuous(config: DataAnalysisConfig) :
+    BaseExceptionContinuous<DataAnalysisConfig, ExceptionResult>(config) {
+
+    override fun newResult(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+    ): ExceptionResult {
+        val eType = getExceptionType()
+        return ExceptionResult(start, end, factor, exceptionData, config.mission.missionCode, eType)
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt
index 3bf7937..f57c775 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt
@@ -1,7 +1,9 @@
 package com.flightfeather.uav.biz.dataanalysis.exceptiontype
 
+import com.flightfeather.uav.biz.FactorFilter
 import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuousSingle
 import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
 import com.flightfeather.uav.socket.eunm.FactorType
@@ -10,7 +12,8 @@
  * 鏁版嵁瓒呮爣寮傚父鍒嗘瀽
  */
 @Deprecated("鍘熻秴鏍囧垽瀹氶�昏緫璁惧畾鐨勮秴鏍囧�兼湁婕忔礊锛�")
-class ExceptionDataExceed(config: DataAnalysisConfig) : BaseExceptionContinuousSingle(config) {
+class ExceptionDataExceed(config: DataAnalysisConfig) :
+    BaseExceptionContinuousSingle<DataAnalysisConfig, ExceptionResult>(config) {
 
     override fun getExceptionType(): ExceptionType = ExceptionType.TYPE2
 
@@ -29,7 +32,17 @@
         return res
     }
 
-    override fun judgeDuration(sIndex: Int, eIndex: Int): Boolean {
+    override fun judgeExceptionCount(tag: Tag): Boolean {
         return true
     }
+
+    override fun newResult(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+    ): ExceptionResult {
+        val eType = getExceptionType()
+        return ExceptionResult(start, end, factor, exceptionData, config.mission.missionCode, eType)
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt
index 391e20d..6ac4ab8 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt
@@ -3,6 +3,7 @@
 import com.flightfeather.uav.biz.FactorFilter
 import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis
 import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
 import com.flightfeather.uav.socket.eunm.FactorType
@@ -11,7 +12,8 @@
 /**
  * 婊戝姩骞冲潎鍊肩獊鍙樺紓甯�
  */
-class ExceptionSlideAverage(config: DataAnalysisConfig) : BaseExceptionAnalysis(config) {
+class ExceptionSlideAverage(config: DataAnalysisConfig) :
+    BaseExceptionAnalysis<DataAnalysisConfig, ExceptionResult>(config) {
 
     private val historyDataList = mutableListOf<BaseRealTimeData>()
     private val tempDataList = mutableListOf<BaseRealTimeData>()
@@ -27,7 +29,7 @@
         var sIndex = 0
 
         // 璧峰鏁版嵁瀵硅薄
-        var startData :BaseRealTimeData? = null
+        var startData: BaseRealTimeData? = null
 
         // 鏈熬鏁版嵁涓嬫爣
         var eIndex = -1
@@ -66,7 +68,7 @@
         lastData = null
 
         tagMap.clear()
-        config.factorFilter.mainList().forEach {f->
+        config.factorFilter.mainList().forEach { f ->
             tagMap[f] = Tag()
         }
 //        avgListReverse.clear()
@@ -86,7 +88,7 @@
         if (tempDataList.size > config.changeTrendGroup) {
             tempDataList.removeAt(0)
         }
-        config.factorFilter.selectedList.forEach {s->
+        config.factorFilter.selectedList.forEach { s ->
             val f = s.main
             tagMap[f]?.let {
                 it.eIndex++
@@ -198,4 +200,14 @@
         }
 
     }
+
+    fun newResult(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+    ): ExceptionResult {
+        val eType = getExceptionType()
+        return ExceptionResult(start, end, factor, exceptionData, config.mission.missionCode, eType)
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt
index e7994b7..582223b 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt
@@ -1,6 +1,5 @@
 package com.flightfeather.uav.biz.dataanalysis.exceptiontype
 
-import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuous
 import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
@@ -10,7 +9,7 @@
 /**
  * 閲忕骇绐佸彉寮傚父鍒嗘瀽
  */
-class ExceptionValueMutation(config: DataAnalysisConfig) : BaseExceptionContinuous(config) {
+class ExceptionValueMutation(config: DataAnalysisConfig) : ExceptionContinuous(config) {
 
     /**
      * 鏈紓甯哥殑杩炵画鍙戠敓娆℃暟浼氭牴鎹紓甯哥殑绋嬪害鍙樺寲
@@ -40,7 +39,11 @@
         return res
     }
 
-    override fun judgeDuration(sIndex: Int, eIndex: Int): Boolean {
+    override fun judgeExceptionCount(tag: Tag): Boolean {
+        // 棣栦釜鏁版嵁娌℃湁鍓嶄竴涓暟鎹弬鐓э紝涓嶇畻寮傚父鍊硷紝鏈�鍚庝竴涓暟鎹槸鍒ゆ柇缁撴潫鐨勬甯稿�硷紝鍥犳寮傚父鏁版嵁涓暟鐨勮绠椾笅鏍囦负sIndex鍜宔Index
+        val sIndex = tag.sIndex
+        val eIndex = tag.eIndex - 1
+
         val b1 = special && (eIndex - sIndex) >= (config.mutationNum / 2)
         val b2 = (eIndex - sIndex) >= config.mutationNum
         special = false
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt
index 033514f..165586f 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt
@@ -1,29 +1,33 @@
 package com.flightfeather.uav.biz.dataanalysis.model
 
 import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseAnalysisConfig
 import com.flightfeather.uav.domain.entity.Mission
 
 /**
  * 鏁版嵁鍒嗘瀽閰嶇疆鍙傛暟
  */
-data class DataAnalysisConfig(
+class DataAnalysisConfig(
     // 璧拌埅浠诲姟淇℃伅
     val mission: Mission,
     // 鏁版嵁寮傚父閰嶇疆
     val exceptionSetting: ExceptionSetting,
     // 鍥犲瓙绛涢��
-    val factorFilter: FactorFilter,
-){
+    factorFilter: FactorFilter,
+) : BaseAnalysisConfig(factorFilter) {
     // 杩炵画绐佸彉鏁版嵁涓暟
     var mutationNum = 2
+
     // 绐佸彉鐜�
     var mutationRate = .5
 
     // 姹傛粦鍔ㄥ钩鍧囧�肩殑鏁版嵁缁勪釜鏁�
     var changeTrendGroup = 12
+
     // 婊戝姩骞冲潎鍊艰繛缁�
     var changeTrendInterval = 12
     var changeTrendRate = .2
+
     // 婊戝姩骞冲潎鍊煎彉鍖栫巼寮傚父杩炵画娆℃暟
     var changeTrendTimes = 3
 
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionResult.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionResult.kt
index 9d80c20..2929675 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionResult.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionResult.kt
@@ -1,16 +1,20 @@
 package com.flightfeather.uav.biz.dataanalysis.model
 
 import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseExceptionResult
+import com.flightfeather.uav.common.utils.DateUtil
 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.socket.eunm.FactorType
 import java.math.BigDecimal
 import java.util.*
 
 /**
  * 寮傚父缁撴灉
  */
-open class ExceptionResult {
+open class ExceptionResult(): BaseExceptionResult() {
     var missionCode: String? = null
     var deviceCode: String? = null
     var exception: String? = null
@@ -40,7 +44,64 @@
     // 鐩稿叧浼佷笟鍚嶇О锛堝悕绉颁箣闂�;鍒嗛殧锛�
     var relatedSceneName: List<String>? = null
     var relatedSceneList: List<SceneInfo?>? = null
-    // 寮傚父鏁版嵁锛屽ご灏惧彲鑳藉寘鍚竴瀹氶噺鐨勫亸绉�
+    // 寮傚父鏁版嵁
     var dataList: MutableList<BaseRealTimeData> = mutableListOf()
     var dataVoList: List<DataVo>? = null
+
+    constructor(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+        missionCode: String?,
+        eType: ExceptionType,
+    ) : this() {
+        this.missionCode = missionCode
+        deviceCode = start.deviceCode
+        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
+        startDate = start.dataTime
+        endDate = end?.dataTime
+        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
+
+        val avgData = exceptionData.avg()
+        // 姹傚彇姹℃煋鏁版嵁鐨勪腑蹇冨潗鏍�
+        longitude = avgData.longitude
+        latitude = avgData.latitude
+        // 姹傚彇涓绘薄鏌撳洜瀛愮殑鍧囧�煎拰鑼冨洿
+        val s = dataSummary(exceptionData, factor.main)
+        avg = s.first
+        min = s.second
+        max = s.third
+
+        exceptionData.forEach { dataList.add(it) }
+    }
+
+    private fun dataSummary(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Triple<Float, Float, Float> {
+        var min = -1f
+        var max = -1f
+        var total = 0f
+        var count = 0
+        exceptionData.forEach {
+            val value = it?.getByFactorType(factorType) ?: return@forEach
+            if (min == -1f || min > value) {
+                min = value
+            }
+            if (max == -1f || max < value) {
+                max = value
+            }
+            total += value
+            count++
+        }
+        val avg = if (count == 0) 0f else total / count
+        return Triple(avg, min, max)
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt
new file mode 100644
index 0000000..6f8a4af
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeAnalysisConfig.kt
@@ -0,0 +1,30 @@
+package com.flightfeather.uav.biz.sourcetrace
+
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseAnalysisConfig
+
+/**
+ * 瀹炴椂璧拌埅姹℃煋婧簮璁$畻鍙傛暟
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+class RealTimeAnalysisConfig(
+    // 鍥犲瓙绛涢��
+    factorFilter: FactorFilter,
+) : BaseAnalysisConfig(factorFilter) {
+
+    // 闄愬畾璺濈鍐咃紙鍗曚綅锛氱背锛�
+    var distanceLimit = 1000
+
+    // 闄愬畾鏃堕棿鍐咃紙鍗曚綅锛氬垎閽燂級
+    var timeLimit = 2
+
+    // 绐佸彉鏁版嵁涓暟
+    var mutationNum = 3
+
+    // 绐佸彉鐜�
+    var mutationRate = .2
+
+    // 婧簮鏈夋晥鏈�澶ч閫燂紝5鍒嗛挓涓嶈秴杩�2鍏噷鐨勯閫燂紙鏆傚畾锛�
+    var sourceTraceWindSpeedLimit = 6.7
+}
\ 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
new file mode 100644
index 0000000..4bd356a
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/RealTimeExceptionAnalysisController.kt
@@ -0,0 +1,71 @@
+package com.flightfeather.uav.biz.sourcetrace
+
+import com.flightfeather.uav.biz.FactorFilter
+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.domain.entity.BaseRealTimeData
+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.socket.handler.UnderwayWebSocketServerHandler
+import com.google.gson.Gson
+
+/**
+ * 瀹炴椂璧拌埅姹℃煋婧簮
+ * @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)
+
+    private val taskList = mutableListOf<BaseExceptionAnalysis<RealTimeAnalysisConfig, RealTimeExceptionResult>>()
+
+    private fun initTask(config: RealTimeAnalysisConfig) {
+        taskList.clear()
+        taskList.apply {
+            add(RealTimeExceptionValueMutation(config){ exceptionCallback(it)})
+        }
+    }
+
+    init {
+        initTask(config)
+    }
+
+    // 璁$畻鍘嗗彶浠诲姟
+    fun addOneData(data: BaseRealTimeData) {
+        taskList
+
+    }
+
+    private fun exceptionCallback(ex: RealTimeExceptionResult) {
+        if (sourceTrace(ex, config)) {
+            underwayWebSocketServerHandler.broadcast(GsonUtils.gson.toJson(ex))
+        }
+    }
+
+    private fun sourceTrace(ex: RealTimeExceptionResult, config: RealTimeAnalysisConfig):Boolean {
+        val avgData = ex.dataList.avg()
+        if (avgData.windSpeed!! > config.sourceTraceWindSpeedLimit) {
+            return false
+        }
+
+        // 鍙栦腑闂寸偣浣滀负鍙嶅悜婧簮鐨勮捣鐐�
+        val midData = ex.dataList[ex.dataList.size / 2]
+
+//        avgData.longitude
+//        avgData.latitude
+//        avgData.windDirection
+        return false
+    }
+
+}
\ 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
new file mode 100644
index 0000000..ce7c9b4
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionContinuous.kt
@@ -0,0 +1,43 @@
+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
new file mode 100644
index 0000000..b87f479
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
@@ -0,0 +1,92 @@
+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
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.socket.eunm.FactorType
+import java.time.Duration
+import java.time.LocalDateTime
+import java.time.ZoneId
+
+/**
+ * 閲忕骇绐佸彉寮傚父婧簮
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+class RealTimeExceptionValueMutation : RealTimeExceptionContinuous {
+
+    constructor(config: RealTimeAnalysisConfig) : super(config)
+
+    constructor(config: RealTimeAnalysisConfig, callback: NewExceptionCallback) : super(config, callback)
+
+    /**
+     * 鏈紓甯哥殑杩炵画鍙戠敓娆℃暟浼氭牴鎹紓甯哥殑绋嬪害鍙樺寲
+     * 褰撶獊鍙樼殑閲忕骇瓒呰繃璁惧畾鍊�1鍊嶆椂,杩炵画鍙戠敓娆℃暟瑕佹眰鍑忓皯1鍊�
+     */
+    private var special = false
+
+    override fun getExceptionType(): ExceptionType {
+        return ExceptionType.TYPE4
+    }
+
+    override fun judgeException(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) {
+                res[f] = (false)
+                return@forEach
+            }
+            val pValue = p.getByFactorType(f)!!
+            val nValue = n.getByFactorType(f)!!
+            // 璁$畻鍚庝竴涓暟鎹浉姣斾簬鍓嶄竴涓暟鎹殑鍙樺寲鐜�
+            val r = (nValue - pValue) / pValue
+            // 褰撳彉鍖栫巼涓烘鏁帮紙鍗虫暟鎹笂鍗囨椂锛夛紝涓斿ぇ浜庤瀹氬�兼椂锛岃涓烘槸寮傚父鎯呭喌
+            val b1 = r >= (2 * config.mutationRate)
+            val b2 = r >= config.mutationRate
+            if (b1) special = true
+            res[f] = (b1 || b2)
+        }
+
+        return res
+    }
+
+    override fun judgeExceptionCount(tag: Tag): Boolean {
+        // 棣栦釜鏁版嵁娌℃湁鍓嶄竴涓暟鎹弬鐓э紝涓嶇畻寮傚父鍊硷紝鏈�鍚庝竴涓暟鎹槸鍒ゆ柇缁撴潫鐨勬甯稿�硷紝鍥犳寮傚父鏁版嵁涓暟鐨勮绠椾笅鏍囦负sIndex鍜宔Index
+        val sIndex = tag.sIndex
+        val eIndex = tag.eIndex - 1
+
+        val b1 = special && (eIndex - sIndex) >= (config.mutationNum / 2)
+        val b2 = (eIndex - sIndex) >= config.mutationNum
+        special = false
+        return b1 || b2
+    }
+
+    override fun needCut(tag: Tag): Boolean {
+        // 鎸夌収鏃堕暱鍜岃窛绂婚檺鍒跺皢寮傚父鎴彇
+        if (tag.exceptionData.isEmpty()) return false
+
+        val se = tag.exceptionData.first()
+        val ee = tag.exceptionData.last()
+
+        val sTime = LocalDateTime.ofInstant(se.dataTime?.toInstant(), ZoneId.systemDefault())
+        val eTime = LocalDateTime.ofInstant(ee.dataTime?.toInstant(), ZoneId.systemDefault())
+        val duration = Duration.between(sTime, eTime).toMinutes()
+        // 鏁版嵁閲囨牱鐨勬椂闀胯秴杩囬檺鍒舵椂锛岄渶瑕佹埅鍙�
+        val b1 = duration > config.timeLimit
+
+        // 璧拌埅鏁版嵁鐨勮窛绂昏秴杩囬檺鍒舵椂锛岄渶瑕佹埅鍙�
+        val b2 = if (se.longitude == null || se.latitude == null || ee.longitude == null || ee.latitude == null) {
+            false
+        } else {
+            val distance = MapUtil.getDistance(
+                se.longitude!!.toDouble(), se.latitude!!.toDouble(), ee.longitude!!
+                    .toDouble(), ee.latitude!!.toDouble()
+            )
+            distance > config.distanceLimit
+        }
+
+        return b1 || b2
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..86ef036
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/RealTimeExceptionResult.kt
@@ -0,0 +1,105 @@
+package com.flightfeather.uav.biz.sourcetrace.model
+
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseExceptionResult
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
+import com.flightfeather.uav.common.utils.DateUtil
+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.socket.eunm.FactorType
+import java.math.BigDecimal
+
+/**
+ *
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+class RealTimeExceptionResult() : BaseExceptionResult() {
+
+    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: Float? = null
+    var endData: Float? = null
+    var avg: Float? = null
+    var min: Float? = null
+    var max: Float? = null
+
+    // 寮傚父鏁版嵁锛屽ご灏惧彲鑳藉寘鍚竴瀹氶噺鐨勫亸绉�
+    var dataList: MutableList<BaseRealTimeData> = mutableListOf()
+
+    // 涓績鐐圭粡绾害
+    var longitude: BigDecimal? = null
+    var latitude: BigDecimal? = null
+
+    // 婧簮浼佷笟
+    var relatedSceneList: List<SceneInfo?>? = null
+
+    constructor(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+        eType: ExceptionType,
+    ) : this() {
+        deviceCode = start.deviceCode
+        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
+
+        val avgData = exceptionData.avg()
+        // 姹傚彇姹℃煋鏁版嵁鐨勪腑蹇冨潗鏍�
+        longitude = avgData.longitude
+        latitude = avgData.latitude
+        // 姹傚彇涓绘薄鏌撳洜瀛愮殑鍧囧�煎拰鑼冨洿
+        val s = dataSummary(exceptionData, factor.main)
+        avg = s.first
+        min = s.second
+        max = s.third
+
+        exceptionData.forEach { dataList.add(it) }
+    }
+
+    private fun dataSummary(
+        exceptionData: List<BaseRealTimeData?>,
+        factorType: FactorType,
+    ): Triple<Float, Float, Float> {
+        var min = -1f
+        var max = -1f
+        var total = 0f
+        var count = 0
+        exceptionData.forEach {
+            val value = it?.getByFactorType(factorType) ?: return@forEach
+            if (min == -1f || min > value) {
+                min = value
+            }
+            if (max == -1f || max < value) {
+                max = value
+            }
+            total += value
+            count++
+        }
+        val avg = if (count == 0) 0f else total / count
+        return Triple(avg, min, max)
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/common/utils/GsonUtils.kt b/src/main/kotlin/com/flightfeather/uav/common/utils/GsonUtils.kt
index 387be46..5585a60 100644
--- a/src/main/kotlin/com/flightfeather/uav/common/utils/GsonUtils.kt
+++ b/src/main/kotlin/com/flightfeather/uav/common/utils/GsonUtils.kt
@@ -1,8 +1,9 @@
 package com.flightfeather.uav.common.utils
 
 import com.google.gson.Gson
+import com.google.gson.GsonBuilder
 import com.google.gson.JsonParser
-import java.util.ArrayList
+import java.time.LocalDateTime
 
 /**
  * @author riku
@@ -11,6 +12,10 @@
  */
 object GsonUtils {
 
+    val gson: Gson = GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss")
+        .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
+        .create()
+
     fun getNoteJsonString(jsonString: String, note: String): String {
         if (jsonString.isEmpty()) {
             throw RuntimeException("getNoteJsonString jsonString empty")
diff --git a/src/main/kotlin/com/flightfeather/uav/common/utils/LocalDateTimeAdapter.java b/src/main/kotlin/com/flightfeather/uav/common/utils/LocalDateTimeAdapter.java
new file mode 100644
index 0000000..73ac7ad
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/common/utils/LocalDateTimeAdapter.java
@@ -0,0 +1,30 @@
+package com.flightfeather.uav.common.utils;
+
+import com.google.gson.*;
+
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * LocalDateTime绫诲瀷鐨勬椂闂存牸寮忓簭鍒楀寲鍜屽弽搴忓垪鍖栫被
+ * by hc 2024.12.6
+ */
+public class LocalDateTimeAdapter implements JsonDeserializer<LocalDateTime>, JsonSerializer<LocalDateTime> {
+    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    @Override
+    public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) {
+        return new JsonPrimitive(dateTimeFormatter.format(src));
+    }
+
+    @Override
+    public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+        try {
+            return LocalDateTime.parse(json.getAsString(), dateTimeFormatter);
+        } catch (Exception e) {
+            throw new JsonParseException(e);
+        }
+    }
+}
+
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 e3e9917..cdcfcb9 100644
--- a/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
+++ b/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
@@ -51,6 +51,7 @@
 
     /**
      * 鑾峰彇涓ょ粡绾害闂寸殑璺濈
+     * @return 杩斿洖涓ょ偣闂磋窛绂伙紝鍗曚綅锛氱背
      */
     fun getDistance(lng1: Double, lat1: Double, lng2: Double, lat2: Double): Double {
 //        lat1 = lat1 || 0;
@@ -167,7 +168,7 @@
      * 鍒ゆ柇鍧愭爣鐐规槸鍚﹀湪澶氳竟褰㈠唴閮�
      */
     fun isPointInPolygon(point: Pair<Double, Double>, polygon: List<Pair<Double, Double>>): Boolean {
-        if (polygon.size < 4) throw IllegalArgumentException("not a polygon")
+        if (polygon.size < 3) throw IllegalArgumentException("not a polygon")
 
         // 涓嶅湪鍥涜嚦鑼冨洿鍐咃紝鍒欎竴瀹氫笉鍦ㄥ杈瑰舰鍐�
         if (!inBBox(point, polygon)) return false
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt b/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
index d4d315c..741a06a 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
@@ -10,6 +10,8 @@
 import java.time.ZoneId
 import java.util.*
 import javax.persistence.Column
+import javax.persistence.GeneratedValue
+import javax.persistence.GenerationType
 import javax.persistence.Id
 import kotlin.math.atan
 import kotlin.math.cos
@@ -21,6 +23,7 @@
  */
 open class BaseRealTimeData {
     @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
     var id: Int? = null
 
     @Column(name = "device_code")
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/repository/AirDataRep.kt b/src/main/kotlin/com/flightfeather/uav/domain/repository/AirDataRep.kt
index 1460a05..20ae724 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/repository/AirDataRep.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/repository/AirDataRep.kt
@@ -1,5 +1,6 @@
 package com.flightfeather.uav.domain.repository
 
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
 import com.flightfeather.uav.domain.entity.RealTimeData
 import com.flightfeather.uav.lightshare.bean.DataVo
 import com.flightfeather.uav.socket.bean.AirDataPackage
@@ -26,6 +27,6 @@
      */
     fun savePrepData(dataList: List<RealTimeData>): Int
 
-    fun savePrepData2(dataList: List<DataVo>): Int
+    fun savePrepData2(dataList: List<DataVo>): List<BaseRealTimeData>
 
 }
\ No newline at end of file
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 6f2bdc1..5008d5b 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
@@ -127,8 +127,8 @@
         return count
     }
 
-    override fun savePrepData2(dataList: List<DataVo>): Int {
-        var count = 0
+    override fun savePrepData2(dataList: List<DataVo>): List<BaseRealTimeData> {
+        val res = mutableListOf<BaseRealTimeData>()
         dataList.forEach {vo ->
             when (UWDeviceType.getType(vo.deviceCode)) {
                 UWDeviceType.VEHICLE -> {
@@ -139,7 +139,7 @@
                     calibration(d, UWDeviceType.VEHICLE)
                     /***************************************************************************************************/
                     realTimeDataVehicleMapper.insert(d)
-                    count++
+                    res.add(d)
                 }
                 UWDeviceType.UAV -> {
                     val d = RealTimeDataUav()
@@ -164,7 +164,7 @@
                     }
                     /***************************************************************************************************/
                     realTimeDataUavMapper.insert(d)
-                    count++
+                    res.add(d)
                 }
                 UWDeviceType.GRID -> {
                     val d = RealTimeDataGrid()
@@ -196,7 +196,7 @@
 //                    d.h2s = d.h2s?.let { sqrt(it) * 2 }
                     /**************************************************************************/
                     realTimeDataGridMapper.insert(d)
-                    count++
+                    res.add(d)
                 }
                 UWDeviceType.BOAT -> {
 
@@ -204,7 +204,7 @@
                 else -> Unit
             }
         }
-        return count
+        return res
     }
 
     private fun dataTransform(vo: RealTimeData, bean: BaseRealTimeData) {
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt
index 8648d99..771ff89 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt
@@ -395,7 +395,7 @@
             println("褰撳墠椤垫暟锛�$page")
             val dataList = res.data ?: emptyList()
             val result = epwDataPrep.mDataPrep2(dataList)
-            count += airDataRep.savePrepData2(result)
+            count += airDataRep.savePrepData2(result).size
             page++
         }
 
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt b/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt
index c9d98ea..1abff63 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/UnderwaySocketServer.kt
@@ -1,5 +1,7 @@
 package com.flightfeather.uav.socket
 
+import com.flightfeather.uav.socket.handler.ServerHandler
+import com.flightfeather.uav.socket.handler.UnderwayWebSocketServerHandler
 import com.flightfeather.uav.socket.processor.BaseProcessor
 import io.netty.bootstrap.ServerBootstrap
 import io.netty.channel.ChannelHandler
@@ -9,15 +11,22 @@
 import io.netty.channel.socket.nio.NioServerSocketChannel
 import io.netty.channel.socket.nio.NioSocketChannel
 import io.netty.handler.codec.LineBasedFrameDecoder
+import io.netty.handler.codec.http.HttpObjectAggregator
+import io.netty.handler.codec.http.HttpServerCodec
+import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler
 import io.netty.handler.codec.string.StringDecoder
 import io.netty.handler.codec.string.StringEncoder
+import org.springframework.stereotype.Component
 import java.nio.charset.Charset
 
 /*********************************************************************************
  * 璧拌埅鐩戞祴鏁版嵁socket闀胯繛鎺ユ湇鍔$
  * 鐢ㄤ簬鎺ユ敹瑙f瀽璧拌埅鐩戞祴鏁版嵁锛屽墠绔洃娴嬭澶囩洰鍓嶅寘鎷溅杞借蛋鑸�佹棤浜烘満璧拌埅浠ュ強鏃犱汉鑸硅蛋鑸笁绉嶇被鍨�
  * *******************************************************************************/
-class UnderwaySocketServer {
+@Component
+class UnderwaySocketServer(
+    private val underwayWebSocketServerHandler:UnderwayWebSocketServerHandler
+) {
 
     private val bossGroup = NioEventLoopGroup()
     private val workerGroup = NioEventLoopGroup()
@@ -28,6 +37,10 @@
 
     fun startElectricServer(port: Int, processor: BaseProcessor) {
         electricServer(processor)?.bind(port)?.sync()
+    }
+
+    fun startWebSocketServer(port: Int, processor: BaseProcessor) {
+        webSocketServer(processor)?.bind(port)?.sync()
     }
 
     fun stopServer() {
@@ -75,4 +88,17 @@
                 ?.addLast(ServerHandler(processor))
         }
     })
+
+    /**
+     * 澶氬弬鏁拌蛋鑸湇鍔$
+     */
+    private fun webSocketServer(processor: BaseProcessor):ServerBootstrap? = newServer(object : ChannelInitializer<NioSocketChannel>() {
+        override fun initChannel(p0: NioSocketChannel?) {
+            p0?.pipeline()
+                ?.addLast(HttpServerCodec())
+                ?.addLast(HttpObjectAggregator(65535))
+                ?.addLast(WebSocketServerProtocolHandler("/ws"))
+                ?.addLast(underwayWebSocketServerHandler)
+        }
+    })
 }
\ 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
new file mode 100644
index 0000000..90808a1
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/socket/handler/BaseHandler.kt
@@ -0,0 +1,59 @@
+package com.flightfeather.uav.socket.handler
+
+import io.netty.channel.ChannelHandlerContext
+import io.netty.channel.ChannelInboundHandlerAdapter
+import java.text.SimpleDateFormat
+import java.util.*
+
+/**
+ * socket娑堟伅澶勭悊绉熀绫�
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+abstract class BaseHandler : ChannelInboundHandlerAdapter() {
+
+    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?) {
+        println("------銆�${tag}銆慖P婵�娲伙細[ip:${ctx?.channel()?.remoteAddress()}] ${
+            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?) {
+        println("------銆�${tag}銆戠鍙f湁IP涓嶆椿鍔細[ip:${ctx?.channel()?.remoteAddress()}] ${
+            SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
+                Date()
+            )}")
+        super.channelInactive(ctx)
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
+        cause?.printStackTrace()
+        ctx?.close()
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt b/src/main/kotlin/com/flightfeather/uav/socket/handler/ServerHandler.kt
similarity index 97%
rename from src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt
rename to src/main/kotlin/com/flightfeather/uav/socket/handler/ServerHandler.kt
index 61a2d8c..30be29a 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/handler/ServerHandler.kt
@@ -1,10 +1,9 @@
-package com.flightfeather.uav.socket
+package com.flightfeather.uav.socket.handler
 
 import com.flightfeather.uav.socket.processor.BaseProcessor
 import io.netty.channel.ChannelHandlerContext
 import io.netty.channel.ChannelInboundHandlerAdapter
 import io.netty.util.AttributeKey
-import org.ietf.jgss.MessageProp
 import java.lang.StringBuilder
 import java.text.SimpleDateFormat
 import java.util.*
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt b/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt
new file mode 100644
index 0000000..0e912d3
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt
@@ -0,0 +1,55 @@
+package com.flightfeather.uav.socket.handler
+
+import io.netty.channel.ChannelHandlerContext
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame
+import org.springframework.stereotype.Component
+
+/**
+ *
+ * @date 2025/5/13
+ * @author feiyu02
+ */
+@Component
+class UnderwayWebSocketServerHandler : BaseHandler() {
+
+    private val sessionPool = mutableMapOf<String?, ChannelHandlerContext?>()
+
+    override var tag: String = "UAV-WS"
+
+    override fun channelRegistered(ctx: ChannelHandlerContext?) {
+        super.channelRegistered(ctx)
+        // 灏嗚繛鎺ュ瓨鍌�
+        if (!sessionPool.containsKey(ctx?.name())) {
+            sessionPool[ctx?.name()] = ctx
+        }
+    }
+
+    override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
+        super.channelRead(ctx, msg)
+
+        when (msg) {
+            is TextWebSocketFrame->{
+                println(msg.text())
+                ctx?.channel()?.writeAndFlush(msg)
+            }
+        }
+    }
+
+    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))
+        }
+    }
+}
\ 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 201bfe4..a649598 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt
@@ -1,12 +1,20 @@
 package com.flightfeather.uav.socket.processor
 
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.sourcetrace.RealTimeExceptionAnalysisController
+import com.flightfeather.uav.common.location.LocationRoadNearby
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
 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.SegmentInfoRep
 import com.flightfeather.uav.socket.bean.AirDataPackage
 import com.flightfeather.uav.socket.decoder.AirDataDecoder
 import com.flightfeather.uav.socket.decoder.DataPackageDecoder
 import com.flightfeather.uav.socket.eunm.AirCommandUnit
+import com.flightfeather.uav.socket.eunm.FactorType
 import com.flightfeather.uav.socket.eunm.UWDeviceType
+import com.flightfeather.uav.socket.handler.UnderwayWebSocketServerHandler
 import io.netty.channel.ChannelHandlerContext
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.stereotype.Component
@@ -21,16 +29,17 @@
  */
 
 @Component
-class UnderwayProcessor : BaseProcessor() {
+class UnderwayProcessor(
+    private val airDataRep: AirDataRep,
+    private val realTimeDataRep: RealTimeDataRep,
+    private val locationRoadNearby: LocationRoadNearby,
+    private val segmentInfoRep: SegmentInfoRep,
+    private val underwayWebSocketServerHandler: UnderwayWebSocketServerHandler,
+) : BaseProcessor() {
 
     companion object {
-        private lateinit var instance: UnderwayProcessor
-
         private const val TAG = "UAV"
     }
-
-    @Autowired
-    lateinit var airDataRep: AirDataRep
 
     private val airDataDecoder = AirDataDecoder.instance
     private val dataPackageDecoder = DataPackageDecoder()
@@ -38,10 +47,24 @@
     // 鏁版嵁棰勫鐞嗗嚱鏁�
     private val dataProcessMap = mutableMapOf<String?, EPWDataPrep>()
 
-    @PostConstruct
-    fun init() {
-        instance = this
-    }
+    // 瀹炴椂璧拌埅姹℃煋婧簮澶勭悊鍣�
+    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()
+        )
 
     override var tag: String = "璧拌埅鐩戞祴"
 
@@ -53,7 +76,11 @@
             //淇濆瓨
             deviceSession.saveDevice(packageData.deviceCode, ctx)
             saveToTxt(msg)
-            saveToDataBase(packageData)
+            saveToDataBase(packageData)?.takeIf { it.isNotEmpty() }?.get(0)?.let {
+                // 灏嗚蛋鑸暟鎹紶鍏ュ紓甯稿鐞嗗櫒
+                realTimeExceptionAnalysisController.addOneData(it)
+            }
+
         } else {
             println("------${TAG}鏁版嵁BCC鏍¢獙澶辫触锛岃垗寮� [${SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())}]")
         }
@@ -62,21 +89,23 @@
     /**
      * 淇濆瓨鑷虫暟鎹簱
      */
-    fun saveToDataBase(dataPackage: AirDataPackage) {
+    fun saveToDataBase(dataPackage: AirDataPackage): List<BaseRealTimeData>? {
         when (dataPackage.commandUnit) {
             AirCommandUnit.AirData.value -> {
                 // 浠son鏍煎紡瀛樺偍鍘熷鏁版嵁
-                instance.airDataRep.saveAirData(dataPackage)
+                airDataRep.saveAirData(dataPackage)
                 // 杩涜棰勫鐞嗗悗锛屽瓨鍌ㄨ嚦瀵瑰簲鏁版嵁琛�
                 if (!dataProcessMap.containsKey(dataPackage.deviceCode)) {
                     // 姣忓彴璁惧鏈夊崟鐙殑鏁版嵁棰勫鐞嗗璞�
                     dataProcessMap[dataPackage.deviceCode] = EPWDataPrep(UWDeviceType.getType(dataPackage.deviceCode))
                 }
-                dataProcessMap[dataPackage.deviceCode]?.run {
+                return dataProcessMap[dataPackage.deviceCode]?.run {
                     val list = this.mDataPrep2(dataPackage)// 鏁版嵁骞虫粦澶勭悊
-                    instance.airDataRep.savePrepData2(list)// 鎸夌収璁惧绫诲瀷瀛樺偍鑷冲搴旀暟鎹〃
+                    airDataRep.savePrepData2(list)// 鎸夌収璁惧绫诲瀷瀛樺偍鑷冲搴旀暟鎹〃
                 }
             }
+
+            else -> return emptyList()
         }
     }
 
@@ -108,7 +137,7 @@
     fun encodeToBytes(msg: String): ByteArray {
         val list = msg.split(" ")
         val bytes = ByteArray(list.size)
-        for (i in 0 until list.size) {
+        for (i in list.indices) {
             bytes[i] = list[i].toInt(16).toByte()
         }
 

--
Gitblit v1.9.3