From e731486b50c4ea6e2d28f302df449b4bd0b2be57 Mon Sep 17 00:00:00 2001 From: Riku <risaku@163.com> Date: 星期一, 02 六月 2025 23:02:59 +0800 Subject: [PATCH] 1. 新增走航动态溯源功能 --- src/main/kotlin/com/flightfeather/uav/socket/sender/WebSocketMessage.kt | 21 + src/test/kotlin/com/flightfeather/uav/biz/dataprocess/DataProcessTest.kt | 11 + src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSourceTest.kt | 33 +++ src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt | 116 ++++++++++ src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt | 6 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt | 24 + src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt | 71 +++++ src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt | 18 - src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt | 29 ++ src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt | 9 src/main/kotlin/com/flightfeather/uav/socket/sender/MsgType.kt | 18 + src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt | 2 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt | 4 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt | 2 src/main/kotlin/com/flightfeather/uav/socket/decoder/UnderwayWebSocketParser.kt | 60 +++++ src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt | 11 src/main/kotlin/com/flightfeather/uav/lightshare/bean/SceneInfoVo.kt | 13 + src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt | 199 ++++++++++++----- 18 files changed, 545 insertions(+), 102 deletions(-) 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 6a44390..1abfb01 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt @@ -20,7 +20,7 @@ it.startData = data } // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇 - if (!isContinue || needCut(it)) { + if (!isContinue || needCut(it, hasException[f])) { recordException(s, it, data) } else { if (hasException[f] == true) { diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt new file mode 100644 index 0000000..cefe129 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt @@ -0,0 +1,116 @@ +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.RealTimeAnalysisConfig +import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig +import com.flightfeather.uav.biz.sourcetrace.exceptiontype.* +import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue +import com.flightfeather.uav.biz.sourcetrace.model.PollutedSummary +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.socket.eunm.FactorType +import com.flightfeather.uav.socket.sender.MsgType +import com.flightfeather.uav.socket.sender.UnderwayWebSocketSender +import java.util.* + +/** + * 姹℃煋绾跨储鎺у埗鍣� + * @date 2025/5/27 + * @author feiyu02 + */ +class SourceTraceController { + + + /** + * 5. 姹℃煋婧愮殑琚壂鎻忔鏁� + * 姣忎竴鍒婚挓瀵瑰巻鍙茬嚎绱㈣繘琛岀粺璁★紝鎻愬嚭浼氬晢寤鸿锛堢姹℃煋婧愯緝杩溿�佹薄鏌撴簮鏁伴噺銆佸嚭鐜版鏁帮級銆佽蛋鑸矾绾胯皟鏁村缓璁紙绂绘薄鏌撴簮杈冭繎銆佽蛋鑸建杩规湭鎺ヨ繎婧簮鍦烘櫙锛� + */ + + constructor(sceneInfoRep: SceneInfoRep, factorFilter: FactorFilter?) { + this.sceneInfoRep = sceneInfoRep + this.config = if (factorFilter != null) + RTExcWindLevelConfig(factorFilter) + else + RTExcWindLevelConfig( + 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() + ) + pollutedSummary = PollutedSummary(config){ summaryCallback(it)} + newTask() + } + + constructor(sceneInfoRep: SceneInfoRep) : this(sceneInfoRep, null) + + private val pollutedSummary:PollutedSummary + + private val sceneInfoRep: SceneInfoRep + + private val config: RTExcWindLevelConfig + + private val taskList = mutableListOf<BaseExceptionAnalysis<RTExcWindLevelConfig, PollutedClue>>() + + fun initTask() { + taskList.clear() + newTask() + pollutedSummary.clear() + } + + private fun newTask() { + taskList.apply { + add(RTExcWindLevel1(config) { exceptionCallback(it) }.also { it.init() }) + add(RTExcWindLevel1_1(config) { exceptionCallback(it) }.also { it.init() }) + add(RTExcWindLevel4(config) { exceptionCallback(it) }.also { it.init() }) + add(RTExcWindLevel6(config) { exceptionCallback(it) }.also { it.init() }) + } + } + + /** + * 璁$畻鏂扮殑涓�鏉″疄鏃惰蛋鑸暟鎹� + */ + fun addOneData(data: BaseRealTimeData) { + // 璁$畻寮傚父 + taskList.forEach { it.onNextData(data) } + pollutedSummary.refreshLatestMonitorData(data) + // 闄愬畾鏃堕棿鍐呮病鏈夋柊鏁版嵁浼犲叆锛屽垯缁撴潫褰撳墠鐨勮绠� + } + + /** + * 瓒呮椂澶勭悊锛岃緝闀挎椂闂存病鏈夋柊鏁版嵁杩涘叆锛岃繘琛屽垵濮嬪寲鎿嶄綔 + */ + 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: PollutedClue) { + // 婧簮姹℃煋婧愪俊鎭� + ex.searchScenes(sceneInfoRep) + + // 璁板綍姹℃煋绾跨储 + pollutedSummary.addClue(ex) + + // 骞挎挱姹℃煋婧簮寮傚父缁撴灉 + UnderwayWebSocketSender.broadcast(MsgType.PolClue.value, ex) + } + + private fun summaryCallback(ex: PollutedSummary.AnalysisResult) { + // 骞挎挱姹℃煋婧簮寮傚父缁撴灉 + UnderwayWebSocketSender.broadcast(MsgType.AnaResult.value, ex) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt index 9ca9419..1e08443 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt @@ -18,9 +18,9 @@ ) // 闄愬畾璺濈鍐咃紙鍗曚綅锛氱背锛� - var distanceLimit = 1000 + var distanceLimit = 3000 // 闄愬畾鏃堕棿鍐咃紙鍗曚綅锛氬垎閽燂級 - var timeLimit = 2 + var timeLimit = 3 // 0 - 1绾ч var windLevelCondition1 = WindLevelCondition( @@ -52,4 +52,9 @@ // 婧簮鎵╂暎鍋忕Щ瑙掑害锛堝崟浣嶏細搴︼級 var sourceTraceDegOffset = 120.0 + + // 瀹氭椂绾跨储鍒嗘瀽鏃堕棿闂撮殧(鍗曚綅锛氬垎閽�) + var analysisPeriod = 15 + // 瀹氭椂鍒嗘瀽闂撮殧涓紝绔嬪嵆杩涜绾跨储鍒嗘瀽鐨勬渶灏忕嚎绱㈤噺(鍗曚綅锛氫釜) + var analysisCount = 3 } \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt index b1010a1..e1fe81d 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt @@ -39,6 +39,7 @@ override fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> { val res = mutableMapOf<FactorType, Boolean>() + println() config.factorFilter.mainList().forEach { f -> if (p?.getByFactorType(f) == null || n.getByFactorType(f) == null || n.windSpeed == null) { res[f] = (false) @@ -48,17 +49,20 @@ val con = windLevelCondition if (n.windSpeed!! in con.windSpeed.first..con.windSpeed.second) { + println("椋庨�燂細${n.windSpeed}锛孾${con.windSpeed.first} - ${con.windSpeed.second}]") val pValue = p.getByFactorType(f)!! val nValue = n.getByFactorType(f)!! // 璁$畻鍚庝竴涓暟鎹浉姣斾簬鍓嶄竴涓暟鎹殑鍙樺寲鐜� val r = (nValue - pValue) / pValue val b1 = r >= con.mutationRate.first + println("鍥犲瓙锛�${f.des}锛屽箙搴︼細${r}锛岄檺瀹氾細${con.mutationRate.first}锛�${b1}") res[f] = b1 } else { res[f] = false } } + return res } diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt index fac9ce7..6f7fd3c 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt @@ -91,7 +91,7 @@ if (distanceRange.first == .0) { result.add(center) } else { - // 浠庡紑濮嬭搴﹀惊鐜绠楀潗鏍囩偣鍊肩粨鏉熻搴︼紝姝ラ暱1掳 + // 浠庡紑濮嬭搴﹀惊鐜绠楀潗鏍囩偣鑷崇粨鏉熻搴︼紝姝ラ暱1掳 var startDeg = sDeg while (startDeg <= eDeg) { val p = MapUtil.getPointByLen(center, distanceRange.first, startDeg * PI / 180) diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt index aa722b8..489e66a 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt @@ -15,16 +15,6 @@ class PollutedData() { /** - * - * 1. 杞1.5m/s鍙婁互涓嬶紝 - * 鍓嶅悗鍊间笂鍗囧箙搴﹀湪50%浠ヤ笂1娆★紝璁や负鏄复杩戝彂鐢�(50绫�) - * 鍓嶅悗鍊间笂鍗囧箙搴﹀湪20%浠ヤ笂1娆★紝璁や负鏄繙璺濈鍙戠敓锛�50绫� - 500绫筹級 - * 1.5 m/s鍙婁互涓嬶紝闈欑ǔ澶╂皵锛屼复杩戝彂鐢�(50绫�) - * 2. 1.6 - 7.9 m/s锛屽墠鍚庡�间笂鍗囧箙搴﹀湪20%浠ヤ笂3娆★紝璁や负鏄繙璺濈鍙戠敓锛�50绫� - 1鍏噷锛� - * 3. 8 - 13.8 m/s 浠ヤ笂锛屽墠鍚庡�间笂鍗囧箙搴﹀湪10%浠ヤ笂3娆★紝璁や负鏄繙璺濈鍙戠敓(50绫� - 2鍏噷) - */ - - /** * 9. 鍏宠仈鍥犲瓙 * a) pm2.5銆乸m10鐗瑰埆楂橈紝涓よ�呭湪鍚勬儏鍐典笅鍚屾灞曠ず锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝姣旈噸瓒婇珮锛岃秺鏈夊彲鑳芥槸椁愰ギ * b) pm10鐗瑰埆楂樸�乸m2.5杈冮珮锛屽ぇ棰楃矑鎵皹姹℃煋锛屽彧灞曠ずpm10锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝宸ュ湴涓轰富 @@ -62,6 +52,8 @@ dataList.add(it) dataVoList.add(it.toDataVo()) } + + calPer() } var deviceCode: String? = null @@ -86,6 +78,8 @@ // 鍥犲瓙閲忕骇鍙樺寲骞呭害 var percentage: Double? = null + // 鍥犲瓙閲忕骇骞冲潎鍙樺寲骞呭害 + var avgPer: Double? = null // 鍙戠敓娆℃暟 var times: Int? = null @@ -93,4 +87,14 @@ // 寮傚父鐩戞祴鏁版嵁 var dataList: MutableList<BaseRealTimeData> = mutableListOf() var dataVoList: MutableList<DataVo> = mutableListOf() + + private fun calPer() { + if (dataList.size < 2) return + + var total = .0 + for (i in 0 until dataList.size - 1) { + total += dataList[i].getByFactorType(selectedFactor!!.main)!! + } + avgPer = total / (dataList.size - 1) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt index 2dc6b58..c948fe3 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt @@ -4,7 +4,13 @@ import com.flightfeather.uav.common.utils.MapUtil import com.flightfeather.uav.domain.entity.SceneInfo import com.flightfeather.uav.domain.repository.SceneInfoRep +import com.flightfeather.uav.lightshare.bean.AreaVo +import com.flightfeather.uav.lightshare.bean.SceneInfoVo +import com.flightfeather.uav.lightshare.eunm.SceneType +import com.flightfeather.uav.socket.eunm.FactorType +import org.springframework.beans.BeanUtils import org.springframework.web.context.ContextLoader +import kotlin.math.min /** * 姹℃煋鏉ユ簮 @@ -20,7 +26,11 @@ */ // 婧簮浼佷笟 - var sceneList:List<SceneInfo?>? = null + var sceneList:List<SceneInfoVo?>? = null + + init { + + } fun searchScenes(pollutedArea: PollutedArea, factor: FactorFilter.SelectedFactor) { ContextLoader.getCurrentWebApplicationContext()?.getBean(SceneInfoRep::class.java)?.run { @@ -35,9 +45,10 @@ // Fixme 2025.5.14: 姹℃煋婧愮殑鍧愭爣鏄珮寰峰湴鍥惧潗鏍囩郴锛堢伀鏄熷潗鏍囩郴锛夛紝鑰岃蛋鑸暟鎹槸WGS84鍧愭爣绯� // 鎸夌収鍖哄煙妫�绱㈠唴閮ㄦ薄鏌撴簮淇℃伅 // 1. 棣栧厛鎸夌収鍥涜嚦鑼冨洿浠庢暟鎹簱鍒濇绛涢�夋薄鏌撴簮锛岄渶瑕佸厛灏嗗潗鏍囪浆鎹负gcj02锛堢伀鏄熷潗鏍囩郴锛夛紝鍥犱负姹℃煋婧愬満鏅俊鎭兘涓烘鍧愭爣绯� - val polygonTmp = pollutedArea.polygon!!.map { - MapUtil.gcj02ToWgs84(it) - } +// val polygonTmp = pollutedArea.polygon!!.map { +// MapUtil.gcj02ToWgs84(it) +// } + val polygonTmp = pollutedArea.polygon!! val fb = MapUtil.calFourBoundaries(polygonTmp) val sceneList = sceneInfoRep.findByCoordinateRange(fb) // 2. 鍐嶇簿纭垽鏂槸鍚﹀湪鍙嶅悜婧簮鍖哄煙澶氳竟褰㈠唴閮� @@ -49,9 +60,57 @@ } } - this.sceneList = result + findClosestStation(sceneInfoRep, result) - TODO("鎸夌収鎵�閫夌洃娴嬪洜瀛愮被鍨嬶紝鍖哄垎姹℃煋婧愮被鍨�") +// TODO("鎸夌収鎵�閫夌洃娴嬪洜瀛愮被鍨嬶紝鍖哄垎姹℃煋婧愮被鍨�") + + } + + /** + * 璁$畻鍙兘鐨勭浉鍏虫薄鏌撳満鏅被鍨� + */ + private fun calFactorType(factor: FactorFilter.SelectedFactor) { +// when (factor.main) { +// FactorType.PM25 -> {} +// +// } + } + + /** + * 璁$畻鏈�杩戠殑鐩戞祴绔欑偣 + */ + private fun findClosestStation(sceneInfoRep: SceneInfoRep, sceneList: List<SceneInfo>) { + val res1 = sceneInfoRep.findByArea(AreaVo().apply { + sceneTypeId = SceneType.TYPE19.value.toString() + }) + + val res2 = sceneInfoRep.findByArea(AreaVo().apply { + sceneTypeId = SceneType.TYPE20.value.toString() + }) + val res = res1.toMutableList().apply { addAll(res2) } + + this.sceneList = sceneList.map { + var minLen = -1.0 + var selectedRes: SceneInfo? = null + res.forEach { r-> + val dis = MapUtil.getDistance( + it.longitude.toDouble(), + it.latitude.toDouble(), + r!!.longitude.toDouble(), + r.latitude.toDouble() + ) + if (minLen < 0 || dis < minLen) { + minLen = dis + selectedRes = r + } + } + val vo = SceneInfoVo() + BeanUtils.copyProperties(it, vo) + vo.closestStation = selectedRes + vo.length = minLen + + return@map vo + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt index 3070bcf..6ec9d46 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt @@ -1,16 +1,14 @@ package com.flightfeather.uav.biz.sourcetrace.model -import com.flightfeather.uav.biz.FactorFilter -import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis -import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig -import com.flightfeather.uav.biz.sourcetrace.exceptiontype.* -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.socket.eunm.FactorType -import com.flightfeather.uav.socket.sender.UnderwayWebSocketSender -import java.util.* +import com.flightfeather.uav.domain.entity.SceneInfo +import java.time.LocalDateTime +import java.util.Timer +import java.util.TimerTask + +// 寮傚父鏁版嵁鐢熸垚鍥炶皟绫� +typealias NewPolluteSummaryCallback = (ex: PollutedSummary.AnalysisResult) -> Unit /** * 姹℃煋鎯呭喌姹囨�� @@ -18,7 +16,7 @@ * @date 2025/5/27 * @author feiyu02 */ -class PollutedSummary { +class PollutedSummary(private val config: RTExcWindLevelConfig, private val callback: NewPolluteSummaryCallback) { /** @@ -26,75 +24,152 @@ * 姣忎竴鍒婚挓瀵瑰巻鍙茬嚎绱㈣繘琛岀粺璁★紝鎻愬嚭浼氬晢寤鸿锛堢姹℃煋婧愯緝杩溿�佹薄鏌撴簮鏁伴噺銆佸嚭鐜版鏁帮級銆佽蛋鑸矾绾胯皟鏁村缓璁紙绂绘薄鏌撴簮杈冭繎銆佽蛋鑸建杩规湭鎺ヨ繎婧簮鍦烘櫙锛� */ - constructor(sceneInfoRep: SceneInfoRep, factorFilter: FactorFilter?) { - this.sceneInfoRep = sceneInfoRep - this.config = if (factorFilter != null) - RTExcWindLevelConfig(factorFilter) - else - RTExcWindLevelConfig( - 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() + /** + * 鍒嗘瀽缁撴灉 + */ + inner class AnalysisResult{ + // 鎸夌収琚壂鎻忔鏁伴檷搴忔帓鍒楃殑姹℃煋婧愬垪琛� + var sortedSceneList: List<Pair<SceneInfo?, Int>>? = null } - constructor(sceneInfoRep: SceneInfoRep) : this(sceneInfoRep, null) + /** + * 瀹炴椂缁熻 + */ + inner class AnalysisStatistic { + // 鎸夌収琚壂鎻忔鏁伴檷搴忔帓鍒楃殑姹℃煋婧愬垪琛� + var sortedSceneList: List<Pair<SceneInfo?, Int>>? = null + } - // 姹℃煋绾跨储 - var clueList = mutableListOf<PollutedClue>() + // 鏈�鏂板疄鏃惰蛋鑸洃娴嬫暟鎹� + val realTimeDataList = mutableListOf<BaseRealTimeData>() - private val sceneInfoRep: SceneInfoRep + // 鏈垎鏋愮殑姹℃煋绾跨储 + val clueList = mutableListOf<PollutedClue>() - private val config: RTExcWindLevelConfig + // 宸插垎鏋愮殑姹℃煋绾跨储 + private val historyClueList = mutableListOf<PollutedClue>() - private val taskList = mutableListOf<BaseExceptionAnalysis<RTExcWindLevelConfig, PollutedClue>>() + // 瀹氭椂姹℃煋鍒嗘瀽浠诲姟鎺у埗 + private var analysisTimer: Timer? = null - fun initTask() { - taskList.clear() - taskList.apply { - add(RTExcWindLevel1(config) { exceptionCallback(it) }.also { it.init() }) - add(RTExcWindLevel1_1(config) { exceptionCallback(it) }.also { it.init() }) - add(RTExcWindLevel4(config) { exceptionCallback(it) }.also { it.init() }) - add(RTExcWindLevel6(config) { exceptionCallback(it) }.also { it.init() }) + // 瀹氭椂姹℃煋鍒嗘瀽浠诲姟 + private val analysisOnTimeTask = object : TimerTask() { + override fun run() { + // 璁板綍浠诲姟杩愯鐘舵�� + analysisTaskIsRunning = true + analysis() + // 璁板綍涓婁竴娆$殑浠诲姟缁撴潫鏃堕棿 + lastAnalysisTime = LocalDateTime.now() + analysisTaskIsRunning = false + } + } + + // 瀹氭椂姹℃煋鍒嗘瀽浠诲姟杩愯鐘舵�� + private var analysisTaskIsRunning = false + + // 涓婁竴娆″畾鏃舵薄鏌撳垎鏋愪换鍔$粨鏉熸椂闂� + private lateinit var lastAnalysisTime: LocalDateTime + + init { + clear() + } + + // 鏂板涓�鏉℃薄鏌撶嚎绱� + fun addClue(pollutedClue: PollutedClue) { + clueList.add(pollutedClue) +// realTimeSummary() + analysisOnClueCount() + } + + // 鍒锋柊褰撳墠鏈�鏂扮殑璧拌埅鐩戞祴鏁版嵁 + fun refreshLatestMonitorData(data: BaseRealTimeData) { +// realTimeDataList.clear() + realTimeDataList.add(data) + } + + fun clear() { + realTimeDataList.clear() + clueList.clear() + historyClueList.clear() + analysisTimer?.cancel() + analysisTimer = null + analysisTaskIsRunning = false + lastAnalysisTime = LocalDateTime.now() + resetAnalysisOnTime() + } + + /** + * 閲嶇疆瀹氭椂鍒嗘瀽绾跨储浠诲姟 + */ + private fun resetAnalysisOnTime() { + // 鍙栨秷鍘熸湁鐨勫垎鏋愪换鍔¤鏃� + analysisTimer?.cancel() + // 浠ュ綋鍓嶆椂闂翠负璧风偣锛岄噸鏂板紑濮嬫柊鐨勪竴杞瓑寰呰鏃� + analysisTimer = Timer() + val period = config.analysisPeriod * 60 * 1000L + analysisTimer?.schedule(analysisOnTimeTask, period, period) + } + + /** + * 鍦ㄥ畾鏃舵薄鏌撶嚎绱㈠垎鏋愪换鍔$瓑寰呭懆鏈熸椂闂村唴锛岃嫢姹℃煋绾跨储閲忚秴杩囪瀹氬�硷紝鐩存帴瑙﹀彂鍒嗘瀽绾跨储浠诲姟 + * 骞堕噸缃畾鏃跺垎鏋愪换鍔� + */ + private fun analysisOnClueCount() { + if (clueList.size >= config.analysisCount && !analysisTaskIsRunning) { + analysisOnTimeTask.run() + resetAnalysisOnTime() } } /** - * 璁$畻鏂扮殑涓�鏉″疄鏃惰蛋鑸暟鎹� + * 瀹炴椂绾跨储缁熻 */ - fun addOneData(data: BaseRealTimeData) { - // 璁$畻寮傚父 - taskList.forEach { it.onNextData(data) } - // 闄愬畾鏃堕棿鍐呮病鏈夋柊鏁版嵁浼犲叆锛屽垯缁撴潫褰撳墠鐨勮绠� + private fun realTimeSummary() { + val statistic = AnalysisStatistic() + // 鍏辨湁澶氬皯鐩稿叧姹℃煋婧愶紝鍝簺姹℃煋婧愯鎵弿娆℃暟杈冨 + val sceneMap = mutableMapOf<String?, Pair<SceneInfo?, Int>>() + clueList.forEach {c-> + c.pollutedSource?.sceneList?.forEach { s-> + if (!sceneMap.containsKey(s?.guid)) { + sceneMap[s?.guid] = s to 1 + } else { + sceneMap[s?.guid] = s to (sceneMap[s?.guid]?.second!! + 1) + } + } + } + val res = sceneMap.entries.sortedBy { it.value.second } + statistic.sortedSceneList = res.map { it.value } } /** - * 瓒呮椂澶勭悊锛岃緝闀挎椂闂存病鏈夋柊鏁版嵁杩涘叆锛岃繘琛屽垵濮嬪寲鎿嶄綔 + * 绾跨储鍒嗘瀽 */ - private fun dealOnTimeout() { - val timer = Timer(true) - timer.schedule(object : TimerTask() { - override fun run() { - TODO("Not yet implemented") + private fun analysis() { + val result = AnalysisResult() + // 鍏辨湁澶氬皯鐩稿叧姹℃煋婧愶紝鍝簺姹℃煋婧愯鎵弿娆℃暟杈冨 + val sceneMap = mutableMapOf<String?, Pair<SceneInfo?, Int>>() + clueList.forEach {c-> + c.pollutedSource?.sceneList?.forEach { s-> + if (!sceneMap.containsKey(s?.guid)) { + sceneMap[s?.guid] = s to 1 + } else { + sceneMap[s?.guid] = s to (sceneMap[s?.guid]?.second!! + 1) + } } - }, 60 * 1000) - timer.cancel() + } + val res = sceneMap.entries.sortedBy { it.value.second } + result.sortedSceneList = res.map { it.value } + + // 褰撳墠鐨勮蛋鑸暟鎹殑瀹氫綅鍜屾薄鏌撴簮璺濈鏄惁鏄�愭笎鎺ヨ繎锛岃嫢璧拌埅杩滅浜嗕富瑕佹薄鏌撴簮锛屾彁绀虹敤鎴疯皟鏁磋蛋鑸矾绾� + + + // 绾跨储鍒嗘瀽瀹屾垚鍚庯紝绉诲姩鑷冲巻鍙茬嚎绱㈠垪琛� + historyClueList.addAll(clueList) + clueList.clear() + realTimeDataList.clear() + + callback(result) +// TODO() } - // 鏁版嵁绐佸彉寮傚父鍥炶皟 - private fun exceptionCallback(ex: PollutedClue) { - // 婧簮姹℃煋婧愪俊鎭� - ex.searchScenes(sceneInfoRep) - clueList - // 骞挎挱姹℃煋婧簮寮傚父缁撴灉 - UnderwayWebSocketSender.broadcast(GsonUtils.gson.toJson(ex)) - } } \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/SceneInfoVo.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/SceneInfoVo.kt new file mode 100644 index 0000000..2353b04 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/SceneInfoVo.kt @@ -0,0 +1,13 @@ +package com.flightfeather.uav.lightshare.bean + +import com.flightfeather.uav.domain.entity.SceneInfo + +class SceneInfoVo : SceneInfo() { + + // 鏈�涓磋繎鐨勭洃娴嬬偣浣� + var closestStation: SceneInfo? = null + + // 璺濈锛堝崟浣嶏細绫筹級锛� + var length: Double? = null + +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt new file mode 100644 index 0000000..594f4a7 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt @@ -0,0 +1,29 @@ +package com.flightfeather.uav.lightshare.eunm + +/** + * 鍦烘櫙绫诲瀷 + * @date 2025/6/2 + * @author feiyu02 + */ +enum class SceneType(val value: Int, val des: String) { + TYPE1(1, "宸ュ湴"), + TYPE2(2, "鐮佸ご"), + TYPE3(3, "姘存偿鎼呮媽绔�"), + TYPE4(4, "宸ヤ笟浼佷笟"), + TYPE5(5, "椁愰ギ"), + TYPE6(6, "姹戒慨"), + TYPE7(7, "闄嶅皹鐐�"), + TYPE8(8, "绌烘皵璐ㄩ噺鐩戞祴鐐�"), + TYPE9(9, "閬撹矾鎵皹鐩戞祴鐐�"), + TYPE10(10, "閬撹矾"), + TYPE11(11, "娌虫祦鏂潰"), + TYPE12(12, "宸ヤ笟鍥尯"), + TYPE13(13, "鏃犲浐瀹氬満鏅�"), + TYPE14(14, "鍫嗗満"), + TYPE15(15, "瀹為獙瀹�"), + TYPE16(16, "绮惧搧灏忓尯"), + TYPE17(17, "鍔犳补绔�"), + TYPE18(18, "鍟嗕笟浣�"), + TYPE19(19, "鍥芥帶鐐�"), + TYPE20(20, "甯傛帶鐐�"), +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/socket/decoder/UnderwayWebSocketParser.kt b/src/main/kotlin/com/flightfeather/uav/socket/decoder/UnderwayWebSocketParser.kt new file mode 100644 index 0000000..46a2cc1 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/socket/decoder/UnderwayWebSocketParser.kt @@ -0,0 +1,60 @@ +package com.flightfeather.uav.socket.decoder + +import com.flightfeather.uav.common.utils.GsonUtils +import com.flightfeather.uav.socket.sender.WebSocketMessage +import org.springframework.util.StringUtils + +object UnderwayWebSocketParser { + + const val START_STR: String = "##" + const val SPLIT_STR: String = "&&" + const val END_STR: String = "%%" + + /** + * 娑堟伅鏍煎紡鏍¢獙 + * @param message 娑堟伅 + * @return 鏍煎紡鏄惁鍚堣 + */ + private fun verificationMessage(message: String): Boolean { + if (message.isEmpty()) { + return false + } + if (!message.startsWith(START_STR)) { + return false + } + if (!message.endsWith(END_STR)) { + return false + } + return true + } + + /** + * 瑙f瀽鍑虹被鍨嬪拰鍐呭 + * @param message socket娑堟伅涓殑data瀛楁 + * @return 瑙f瀽缁撴灉锛屽鏋滄牸寮忎笉姝g‘鍒欒繑鍥瀗ull + */ + fun decodeMessage(message: String): WebSocketMessage { + if (!verificationMessage(message)) { + // 鍙戞尌涓�涓笉浼氳澶勭悊鐨勬秷鎭� + return WebSocketMessage(-1, "") + } + val webSocketMessage = WebSocketMessage() + val parts: Array<String> = message.substring(START_STR.length, message.length - END_STR.length) + .split(SPLIT_STR.toRegex()) + .dropLastWhile { it.isEmpty() }.toTypedArray() + webSocketMessage.type = parts[0].toInt() + webSocketMessage.content = GsonUtils.gson.fromJson(parts[1], Any::class.java) + + return webSocketMessage + } + + /** + * 鐢熸垚鎸囧畾鏍煎紡鐨勬秷鎭瓧绗︿覆 + * @return 鐢熸垚鐨勬秷鎭瓧绗︿覆 + */ + fun encodeMessage(webSocketMessage: WebSocketMessage): String { + return START_STR + webSocketMessage.type + SPLIT_STR + + GsonUtils.gson.toJson(webSocketMessage.content, webSocketMessage.content?.javaClass) + + END_STR + } +} \ No newline at end of file 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 fef4d1a..23e7b70 100644 --- a/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt +++ b/src/main/kotlin/com/flightfeather/uav/socket/handler/UnderwayWebSocketServerHandler.kt @@ -1,16 +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.biz.sourcetrace.SourceTraceController 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 /** * @@ -19,7 +16,7 @@ */ class UnderwayWebSocketServerHandler(sceneInfoRep: SceneInfoRep) : BaseHandler() { - private val realTimeExceptionAnalysisController = RealTimeExceptionAnalysisController(sceneInfoRep) + private val sourceTraceController = SourceTraceController(sceneInfoRep) override var tag: String = "UAV-WS" @@ -41,11 +38,11 @@ // Test try { if (msgTxt == "start") { - realTimeExceptionAnalysisController.initTask() + sourceTraceController.initTask() } else { val data = GsonUtils.parserJsonToArrayBeans(msgTxt, DataVo::class.java) data.forEach { - realTimeExceptionAnalysisController.addOneData( + sourceTraceController.addOneData( it.toBaseRealTimeData(BaseRealTimeData::class.java) ) } 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 6c5f374..7d8fd21 100644 --- a/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt +++ b/src/main/kotlin/com/flightfeather/uav/socket/processor/UnderwayProcessor.kt @@ -1,27 +1,19 @@ 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.biz.sourcetrace.SourceTraceController 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.SceneInfoRep -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 import java.text.SimpleDateFormat import java.util.* -import javax.annotation.PostConstruct /** * 澶勭悊socket鎺ユ敹鐨勬秷鎭� @@ -46,7 +38,7 @@ private val dataProcessMap = mutableMapOf<String?, EPWDataPrep>() // 瀹炴椂璧拌埅姹℃煋婧簮澶勭悊鍣� - private val realTimeExceptionAnalysisMap = mutableMapOf<String?, RealTimeExceptionAnalysisController>() + private val sourceTraceMap = mutableMapOf<String?, SourceTraceController>() override var tag: String = "璧拌埅鐩戞祴" @@ -60,11 +52,11 @@ saveToTxt(msg) saveToDataBase(packageData)?.takeIf { it.isNotEmpty() }?.get(0)?.let { // 姣忓彴璁惧鏈夊悇鑷崟鐙殑寮傚父鏁版嵁澶勭悊鍣� - if (!realTimeExceptionAnalysisMap.containsKey(it.deviceCode)) { - realTimeExceptionAnalysisMap[it.deviceCode] = RealTimeExceptionAnalysisController(sceneInfoRep) + if (!sourceTraceMap.containsKey(it.deviceCode)) { + sourceTraceMap[it.deviceCode] = SourceTraceController(sceneInfoRep) } // 灏嗚蛋鑸暟鎹紶鍏ュ紓甯稿鐞嗗櫒 - realTimeExceptionAnalysisMap[it.deviceCode]?.addOneData(it) + sourceTraceMap[it.deviceCode]?.addOneData(it) } } else { diff --git a/src/main/kotlin/com/flightfeather/uav/socket/sender/MsgType.kt b/src/main/kotlin/com/flightfeather/uav/socket/sender/MsgType.kt new file mode 100644 index 0000000..be86205 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/socket/sender/MsgType.kt @@ -0,0 +1,18 @@ +package com.flightfeather.uav.socket.sender + +import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue +import com.flightfeather.uav.biz.sourcetrace.model.PollutedSummary + +enum class MsgType(val value: Int) { + /** + * 姹℃煋绾跨储 + * @see [PollutedClue] + */ + PolClue(1), + + /** + * 姹℃煋鍒嗘瀽缁撴灉 + * @see [PollutedSummary.AnalysisResult] + */ + AnaResult(2), +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt b/src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt index 2a765fe..06fa873 100644 --- a/src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt +++ b/src/main/kotlin/com/flightfeather/uav/socket/sender/UnderwayWebSocketSender.kt @@ -1,5 +1,6 @@ package com.flightfeather.uav.socket.sender +import com.flightfeather.uav.socket.decoder.UnderwayWebSocketParser import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.http.websocketx.TextWebSocketFrame @@ -28,6 +29,11 @@ } + fun broadcast(type:Int, content:Any) { + val msg = UnderwayWebSocketParser.encodeMessage(WebSocketMessage(type,content)) + broadcast(msg) + } + fun broadcast(msg: String) { sessionPool.forEach { (t, u) -> u?.channel()?.writeAndFlush(TextWebSocketFrame(msg)) diff --git a/src/main/kotlin/com/flightfeather/uav/socket/sender/WebSocketMessage.kt b/src/main/kotlin/com/flightfeather/uav/socket/sender/WebSocketMessage.kt new file mode 100644 index 0000000..3b810da --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/socket/sender/WebSocketMessage.kt @@ -0,0 +1,21 @@ +package com.flightfeather.uav.socket.sender + +class WebSocketMessage { + + /** + * 娑堟伅绫诲瀷 + */ + var type: Int = 0 + + /** + * 娑堟伅鍐呭 + */ + var content: Any? = null + + constructor() + + constructor(type: Int, content: Any?) { + this.type = type + this.content = content + } +} diff --git a/src/test/kotlin/com/flightfeather/uav/biz/dataprocess/DataProcessTest.kt b/src/test/kotlin/com/flightfeather/uav/biz/dataprocess/DataProcessTest.kt index 7b51ffb..0a77946 100644 --- a/src/test/kotlin/com/flightfeather/uav/biz/dataprocess/DataProcessTest.kt +++ b/src/test/kotlin/com/flightfeather/uav/biz/dataprocess/DataProcessTest.kt @@ -1,12 +1,15 @@ package com.flightfeather.uav.biz.dataprocess import com.flightfeather.uav.domain.mapper.RealTimeDataMapper +import com.flightfeather.uav.domain.repository.SceneInfoRep +import com.flightfeather.uav.lightshare.bean.AreaVo import com.flightfeather.uav.lightshare.service.RealTimeDataService 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 +import org.springframework.web.context.ContextLoader import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.* @@ -56,4 +59,12 @@ process.outPutDailyVariation() process.done() } + + @Test + fun foo2() { + ContextLoader.getCurrentWebApplicationContext()?.getBean(SceneInfoRep::class.java)?.run { + val res = this.findByArea(AreaVo().apply { sceneTypeId = "20" }) + res.forEach { println(it?.name) } + } + } } \ No newline at end of file diff --git a/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSourceTest.kt b/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSourceTest.kt new file mode 100644 index 0000000..f6ceaa0 --- /dev/null +++ b/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSourceTest.kt @@ -0,0 +1,33 @@ +package com.flightfeather.uav.biz.sourcetrace.model + +import com.flightfeather.uav.biz.FactorFilter +import com.flightfeather.uav.domain.repository.SceneInfoRep +import com.flightfeather.uav.socket.eunm.FactorType +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 + +@RunWith(SpringRunner::class) +@SpringBootTest +class PollutedSourceTest { + + @Autowired + lateinit var sceneInfoRep: SceneInfoRep + + @Test + fun foo1() { + val source = PollutedSource() + val pollutedArea = PollutedArea().apply { + polygon = listOf( + 121.421521 to 31.195457, + 121.421721 to 31.195457, + 121.421521 to 31.195257, + 121.421721 to 31.195257, + ) + } + source.searchScenes(pollutedArea, sceneInfoRep, FactorFilter.SelectedFactor(FactorType.VOC)) + + } +} \ No newline at end of file -- Gitblit v1.9.3