From 4d065a305b997bfb66f41b33a31d59de63b1958d Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期四, 29 五月 2025 17:43:21 +0800
Subject: [PATCH] 1. 新增动态污染溯源新的判定逻辑(待完成)
---
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt | 17 +
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt | 55 ++--
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt | 67 +++++
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt | 38 +++
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt | 81 ++----
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt | 17 +
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt | 17 +
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt | 55 ++++
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt | 17 +
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt | 3
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt | 1
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt | 14
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt | 49 +++
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt | 121 ++++++++++
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt | 101 ++++++++
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt | 11
16 files changed, 573 insertions(+), 91 deletions(-)
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 bf4f1d1..658c621 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -19,42 +19,6 @@
private const val OFFSET = 10
}
- inner class Tag {
- // 璧峰鏁版嵁涓嬫爣
- var sIndex = 0
-
- // 璧峰鏁版嵁瀵硅薄
- var startData: BaseRealTimeData? = null
-
- // 鏈熬鏁版嵁涓嬫爣
- var eIndex = -1
-
- // 鏈熬鏁版嵁瀵硅薄
- var endData: BaseRealTimeData? = null
-
- // 寮傚父鏁版嵁娈�
- var exceptionData = mutableListOf<BaseRealTimeData>()
-
- // 鏄惁瀛樺湪寮傚父
- var exceptionExisted = false
-
- // 寮傚父缁撴灉鏄惁鍒涘缓
- var exceptionCreated = false
-
- fun addExceptionData(data: BaseRealTimeData) {
- exceptionExisted = true
- exceptionData.add(data)
- }
-
- fun refreshWithNextException(data: BaseRealTimeData) {
- sIndex = eIndex
- startData = data
- exceptionData.clear()
- exceptionExisted = false
- exceptionCreated = false
- }
- }
-
protected val tagMap = mutableMapOf<FactorType, T>()
// 璧峰鏁版嵁涓庢湯灏炬暟鎹棿闅�
@@ -62,6 +26,20 @@
// 鏈熬鏁版嵁瀵硅薄
protected var lastData: BaseRealTimeData? = null
+
+ /**
+ * 鍚庣疆鍒ゆ柇锛氬綋鐩搁偦鏁版嵁鏃堕棿涓嶈繛缁椂锛屾垨鑰呮弧瓒宠嚜瀹氫箟鏉′欢鏃讹紝瀵逛箣鍓嶅凡鏈夌殑寮傚父杩涜璁板綍
+ */
+ open fun afterExcCheck(isContinue: Boolean, tag: T, hasException: Boolean?): Boolean {
+ return !isContinue || needCut(tag, hasException)
+ }
+
+ /**
+ * 绔嬪嵆鍒ゆ柇锛氬綋鍑虹幇寮傚父鏃讹紝缂撳瓨寮傚父鏁版嵁鐨勫悓鏃讹紝绔嬪嵆瀵瑰凡鏈夊紓甯歌繘琛屽垽鏂槸鍚︽弧瓒冲紓甯哥粨鏋滆姹�
+ */
+ open fun immeExcCheck(tag: T): Boolean {
+ return false
+ }
/**
* 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画
@@ -75,7 +53,7 @@
}
/**
- * 鍒ゆ柇鏄惁婊¤冻寮傚父鏉′欢
+ * 鍒ゆ柇鍓嶅悗鏁版嵁鏄惁婊¤冻寮傚父鏉′欢
*/
abstract fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean>
@@ -87,11 +65,11 @@
/**
* 寮傚父鏁版嵁鐨勬埅鍙栧垽鏂�
- * 鏄惁闇�瑕侀檺鍒朵竴缁勫紓甯告暟鎹殑闀垮害
* @return 榛樿涓嶉渶瑕佹埅鍙�
*/
- open fun needCut(tag: T): Boolean {
- return false
+ open fun needCut(tag: T, hasException: Boolean?): Boolean {
+ // 榛樿鍒ゆ柇鏉′欢涓� 褰撳紓甯镐笉鍐嶉噸澶嶅嚭鐜版椂锛屽舰鎴愬紓甯哥粨鏋�
+ return tag.exceptionExisted && hasException == false
}
override fun init() {
@@ -115,15 +93,19 @@
if (it.startData == null) {
it.refreshWithNextException(data)
}
- // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇
- if (!isContinue || needCut(it)) {
- // 鏁版嵁涓嶈繛缁椂锛岃褰曞紓甯告儏鍐�
+
+ // 瀵逛簬寮傚父鐨勭敓鎴愬垎鍒墽琛屽悗缃垽鏂�佸拰绔嬪嵆鍒ゆ柇
+ // 1. 鍚庣疆鍒ゆ柇锛氬綋鐩搁偦鏁版嵁鏃堕棿涓嶈繛缁椂锛屾垨鑰呮弧瓒宠嚜瀹氫箟鏉′欢鏃讹紝瀵逛箣鍓嶅凡鏈夌殑寮傚父杩涜璁板綍锛屽舰鎴愬紓甯哥粨鏋�
+ if (afterExcCheck(isContinue, it, hasException[f])) {
+ // 鏁版嵁涓嶈繛缁椂鎴栬�呮弧瓒充富鍔ㄦ埅鏂潯浠舵椂锛岃褰曞紓甯告儏鍐�
recordException(s, it, data)
- } else {
- if (hasException[f] == true) {
- it.addExceptionData(data)
- } else {
- // 寮傚父涓嶅啀閲嶅鍑虹幇鏃讹紝璁板綍寮傚父鎯呭喌
+ }
+ // 2. 绔嬪嵆鍒ゆ柇锛氬綋鍑虹幇寮傚父鏃讹紝缂撳瓨寮傚父鏁版嵁鐨勫悓鏃讹紝绔嬪嵆瀵瑰凡鏈夊紓甯歌繘琛屽垽鏂槸鍚︽弧瓒冲紓甯哥粨鏋滆姹�
+ else if (hasException[f] == true) {
+ // 鏈夊紓甯稿嚭鐜版椂锛岃褰曞紓甯告暟鎹�
+ it.addExceptionData(data)
+ // 褰撶珛鍗冲垽鏂�氳繃鏃讹紝褰㈡垚寮傚父缁撴灉
+ if (immeExcCheck(it)) {
recordException(s, it, data)
}
}
@@ -138,12 +120,11 @@
/**
* 寮傚父缁撴潫锛岃褰曞紓甯�
+ * 鍒ゆ柇宸叉湁鐨勫紓甯告暟鎹槸鍚︽弧瓒冲紓甯告潯浠讹紝婊¤冻鍒欒褰曪紝涓嶆弧瓒冲垯鐣ヨ繃
*/
fun recordException(factor: FactorFilter.SelectedFactor, tag: T, data: BaseRealTimeData) {
checkResult(factor, ExceptionStatusType.Ended)
-// if (tag.eIndex - tag.sIndex >= durationCount) {
tag.refreshWithNextException(data)
-// }
}
/**
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
new file mode 100644
index 0000000..9ca9419
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt
@@ -0,0 +1,55 @@
+package com.flightfeather.uav.biz.sourcetrace.config
+
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseAnalysisConfig
+import com.flightfeather.uav.biz.sourcetrace.model.DistanceType
+
+/**
+ *
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevelConfig(factorFilter: FactorFilter): BaseAnalysisConfig(factorFilter) {
+
+ inner class WindLevelCondition(
+ val windSpeed: Pair<Double, Double>,
+ val mutationRate: Pair<Double, DistanceType>,
+ val countLimit: Int,
+ )
+
+ // 闄愬畾璺濈鍐咃紙鍗曚綅锛氱背锛�
+ var distanceLimit = 1000
+ // 闄愬畾鏃堕棿鍐咃紙鍗曚綅锛氬垎閽燂級
+ var timeLimit = 2
+
+ // 0 - 1绾ч
+ var windLevelCondition1 = WindLevelCondition(
+ .0 to 1.5,
+ 0.5 to DistanceType.TYPE1,
+ 1
+ )
+
+ // 0 - 1绾ч
+ var windLevelCondition1_1 = WindLevelCondition(
+ .0 to 1.5,
+ 0.2 to DistanceType.TYPE2,
+ 1
+ )
+
+ // 2 - 4绾ч
+ var windLevelCondition2 = WindLevelCondition(
+ 1.6 to 7.9,
+ 0.2 to DistanceType.TYPE3,
+ 3
+ )
+
+ // 5 - 6绾ч
+ var windLevelCondition3 = WindLevelCondition(
+ 8.0 to 13.8,
+ 0.1 to DistanceType.TYPE4,
+ 3
+ )
+
+ // 婧簮鎵╂暎鍋忕Щ瑙掑害锛堝崟浣嶏細搴︼級
+ var sourceTraceDegOffset = 120.0
+}
\ 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
new file mode 100644
index 0000000..b1010a1
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt
@@ -0,0 +1,121 @@
+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.ExceptionTag
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
+import com.flightfeather.uav.socket.eunm.FactorType
+import java.time.Duration
+import java.time.LocalDateTime
+import java.time.ZoneId
+
+
+// 寮傚父鏁版嵁鐢熸垚鍥炶皟绫�
+typealias NewPolluteClueCallback = (ex: PollutedClue) -> Unit
+/**
+ * 涓嶅悓椋庨�熶笅锛屾暟鎹獊鍙樺紓甯稿熀绫�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+abstract class BaseRTExcWindLevel(config: RTExcWindLevelConfig) :
+ BaseExceptionContinuous<ExceptionTag, RTExcWindLevelConfig, PollutedClue>(config, ExceptionTag::class.java) {
+
+ constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : this(config){
+ this.callback = callback
+ }
+
+ private var callback: NewPolluteClueCallback? = null
+
+ abstract var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition
+
+ 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 || n.windSpeed == null) {
+ res[f] = (false)
+ return@forEach
+ }
+
+ val con = windLevelCondition
+
+ if (n.windSpeed!! in 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
+ res[f] = b1
+ } else {
+ res[f] = false
+ }
+ }
+
+ return res
+ }
+
+ override fun judgeExceptionCount(tag: ExceptionTag): Boolean {
+ return tag.exceptionData.size >= windLevelCondition.countLimit
+ }
+
+ override fun needCut(tag: ExceptionTag, hasException: Boolean?): 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
+ }
+
+ override fun immeExcCheck(tag: ExceptionTag): Boolean {
+ // 寮傚父鍑虹幇绛変簬闄愬畾娆℃暟鏃讹紝灏遍渶瑕佸舰鎴愭薄鏌撶嚎绱�
+ return tag.exceptionData.size == windLevelCondition.countLimit
+ }
+
+ override fun newResult(
+ start: BaseRealTimeData,
+ end: BaseRealTimeData?,
+ factor: FactorFilter.SelectedFactor,
+ exceptionData: List<BaseRealTimeData>,
+ ): PollutedClue {
+ return PollutedClue(start, end, factor, exceptionData, getExceptionType(), config, windLevelCondition)
+ }
+
+ override fun onNewException(
+ tag: ExceptionTag,
+ factor: FactorFilter.SelectedFactor,
+ exceptionStatus: ExceptionStatusType,
+ ) {
+ super.onNewException(tag, factor, exceptionStatus)
+ callback?.let { func ->
+ val exc = tag.exceptionResult.last()
+ func.invoke(exc as PollutedClue)
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
index bfb6b9a..d4c0232 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
@@ -17,6 +17,7 @@
* @date 2025/5/13
* @author feiyu02
*/
+@Deprecated("2025.5.29, 閫昏緫涓庝笟鍔′笉鍖归厤锛屽悗缁垹闄�")
abstract class BaseRealTimeException<T : ExceptionTag>(config: RealTimeAnalysisConfig, tagClz: Class<T>) :
BaseExceptionContinuous<T, RealTimeAnalysisConfig, RealTimeExceptionResult>(config, tagClz) {
@@ -26,33 +27,33 @@
var callback: NewExceptionCallback? = null
- override fun onNextData(data: BaseRealTimeData) {
- val isContinue = isContinuous(lastData, data)
- val hasException = judgeException(lastData, data)
- config.factorFilter.selectedList.forEach { s ->
- val f = s.main
- tagMap[f]?.let {
- it.eIndex++
- // 璧峰鏁版嵁
- it.endData = data
- if (it.startData == null) {
- it.refreshWithNextException(data)
- }
- // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画鎴栬�呮槸鍚︽弧瓒宠嚜瀹氫箟鎴彇鏉′欢
- if (!isContinue || needCut(it)) {
- // 璁板綍寮傚父锛岀粨鏉熷紓甯哥殑瀹炴椂鐘舵�佹挱鎶�
- recordException(s, it, data)
- } else {
- // 绉婚櫎浜嗙埗绫诲師鏈夐�昏緫锛屾敼涓哄綋婊¤冻寮傚父鏉′欢鏃讹紝闇�瑕佸疄鏃舵帹閫佹挱鎶ュ紓甯哥殑鐘舵�佸彉鍖栵紝浣嗕笉鎴彇寮傚父
- if (hasException[f] == true) {
- it.addExceptionData(data)
- checkResult(s)
- }
- }
- }
- }
- lastData = data
- }
+// override fun onNextData(data: BaseRealTimeData) {
+// val isContinue = isContinuous(lastData, data)
+// val hasException = judgeException(lastData, data)
+// config.factorFilter.selectedList.forEach { s ->
+// val f = s.main
+// tagMap[f]?.let {
+// it.eIndex++
+// // 璧峰鏁版嵁
+// it.endData = data
+// if (it.startData == null) {
+// it.refreshWithNextException(data)
+// }
+// // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画鎴栬�呮槸鍚︽弧瓒宠嚜瀹氫箟鎴彇鏉′欢
+// if (!isContinue || needCut(it)) {
+// // 璁板綍寮傚父锛岀粨鏉熷紓甯哥殑瀹炴椂鐘舵�佹挱鎶�
+// recordException(s, it, data)
+// } else {
+// // 绉婚櫎浜嗙埗绫诲師鏈夐�昏緫锛屾敼涓哄綋婊¤冻寮傚父鏉′欢鏃讹紝闇�瑕佸疄鏃舵帹閫佹挱鎶ュ紓甯哥殑鐘舵�佸彉鍖栵紝浣嗕笉鎴彇寮傚父
+// if (hasException[f] == true) {
+// it.addExceptionData(data)
+// checkResult(s)
+// }
+// }
+// }
+// }
+// lastData = data
+// }
override fun newResult(
start: BaseRealTimeData,
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt
new file mode 100644
index 0000000..63c48fc
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 0 - 1绾ч锛岃蒋椋庯紙椋庨��1.5m/s鍙婁互涓嬶級鐘舵�佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel1 : BaseRTExcWindLevel {
+
+ constructor(config: RTExcWindLevelConfig) : super(config)
+
+ constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+ override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition1
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt
new file mode 100644
index 0000000..adc39e8
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 0 - 1绾ч锛岃蒋椋庯紙椋庨��1.5m/s鍙婁互涓嬶級鐘舵�佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel1_1 : BaseRTExcWindLevel {
+
+ constructor(config: RTExcWindLevelConfig) : super(config)
+
+ constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+ override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition1_1
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt
new file mode 100644
index 0000000..2f25b83
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 2 - 4绾ч锛屽拰椋庯紙椋庨��1.6 - 7.9m/s鍙婁互涓嬶級鐘舵�佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel4:BaseRTExcWindLevel {
+
+ constructor(config: RTExcWindLevelConfig) : super(config)
+
+ constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+ override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition2
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt
new file mode 100644
index 0000000..ef7f8eb
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 4 - 6绾ч锛屽己椋庯紙椋庨��8 - 13.8m/s锛夌姸鎬佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel6:BaseRTExcWindLevel {
+
+ constructor(config: RTExcWindLevelConfig) : super(config)
+
+ constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+ override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition3
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt
index 8e1aba1..f471a79 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt
@@ -19,6 +19,7 @@
* @date 2025/5/13
* @author feiyu02
*/
+@Deprecated("2025.5.29, 閫昏緫涓庝笟鍔′笉鍖归厤锛屽悗缁垹闄�")
class RealTimeExceptionSlideAverage : BaseExceptionAnalysis<RealTimeAnalysisConfig, RealTimeExceptionResult> {
constructor(config: RealTimeAnalysisConfig) : super(config)
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
index 5a97f66..81bc08a 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
@@ -15,6 +15,7 @@
* @date 2025/5/13
* @author feiyu02
*/
+@Deprecated("2025.5.29, 閫昏緫涓庝笟鍔′笉鍖归厤锛屽悗缁垹闄�")
class RealTimeExceptionValueMutation : BaseRealTimeException<ExceptionTag> {
constructor(config: RealTimeAnalysisConfig) : super(config, ExceptionTag::class.java)
@@ -61,7 +62,7 @@
return b1 || b2
}
- override fun needCut(tag: ExceptionTag): Boolean {
+ override fun needCut(tag: ExceptionTag, hasException: Boolean?): Boolean {
// 鎸夌収鏃堕暱鍜岃窛绂婚檺鍒跺皢寮傚父鎴彇
if (tag.exceptionData.isEmpty()) return false
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt
index 4f7af43..37c753a 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt
@@ -5,9 +5,13 @@
* @date 2025/5/28
* @author feiyu02
*/
-enum class DistanceType(val des: String) {
- TYPE1("50绫�"),
- TYPE2("50绫� - 500绫�"),
- TYPE3("50绫� - 1鍏噷"),
- TYPE4("50绫� - 2鍏噷")
+enum class DistanceType(val des: String, val disRange: Pair<Double, Double>) {
+ TYPE1("50绫�", .0 to 50.0),
+ TYPE2("50绫� - 500绫�", 50.0 to 500.0),
+ TYPE3("50绫� - 1鍏噷", 50.0 to 1000.0),
+ TYPE4("50绫� - 2鍏噷", 50.0 to 2000.0);
+
+ override fun toString(): String {
+ return this.des
+ }
}
\ No newline at end of file
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 bcfe4e2..fac9ce7 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
@@ -1,18 +1,115 @@
package com.flightfeather.uav.biz.sourcetrace.model
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+import com.flightfeather.uav.common.net.AMapService
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.avg
+import kotlin.math.PI
+
/**
* 鍔ㄦ�佹函婧愭薄鏌撳尯鍩�
* 閫氳繃鍦板浘鍧愭爣鐐瑰舰鎴愬杈瑰舰鏉ユ弿杩颁竴鍧楁薄鏌撳尯鍩�
* @date 2025/5/27
* @author feiyu02
*/
-class PollutedArea {
+class PollutedArea() {
- var name: String? = null
+ /**
+ * 婧簮瑙掑害鍙缃�
+ */
+
+ constructor(
+ exceptionData: List<BaseRealTimeData>,
+ config: RTExcWindLevelConfig,
+ windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
+ ) : this() {
+ distanceType = windLevelCondition.mutationRate.second
+ sourceTrace(exceptionData, config, windLevelCondition)
+ }
+
+ var address: String? = null
// 姹℃煋鑼冨洿鍖哄煙(缁忕含搴﹀杈瑰舰)
var polygon: List<Pair<Double, Double>>? = null
// 姹℃煋鍙兘鐨勫彂鐢熻窛绂�
var distanceType: DistanceType? = null
+
+ /**
+ * 鍙嶅悜婧簮
+ */
+ private fun sourceTrace(
+ exceptionData: List<BaseRealTimeData>,
+ config: RTExcWindLevelConfig,
+ windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
+ ) {
+ val avgData = if (exceptionData.size == 1) {
+ exceptionData.first()
+ } else {
+ exceptionData.avg()
+ }
+
+ val pair = avgData.longitude!!.toDouble() to avgData.latitude!!.toDouble()
+
+ polygon = calSector(
+ avgData.windSpeed!!.toDouble(),
+ pair,
+ windLevelCondition.mutationRate.second.disRange,
+ config.sourceTraceDegOffset
+ )
+
+ try {
+ val address = AMapService.reGeo(pair)
+ this.address = address.township + address.street
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ }
+
+ /**
+ * 鏍规嵁涓績鐐瑰潗鏍囥�侀鍚戝拰婧簮璺濈锛屼互鍙婄粰瀹氱殑澶硅锛岃绠椾互涓績鐐规寜鐓ч鍚戝悜澶栨墿鏁e舰鎴愮殑鎵囧舰鐨勭偣鍧愭爣
+ * @param windDir 椋庡悜锛屽崟浣嶏細搴�
+ * @param center 涓績鐐瑰潗鏍囩粡绾害
+ * @param distanceRange 婧簮璺濈鑼冨洿锛屽崟浣嶏細绫�
+ * @param defaultDegOffset 鎵╂暎鍋忕Щ瑙掑害
+ * @return 澶氳竟褰㈤《鐐瑰潗鏍囬泦鍚�
+ */
+ private fun calSector(
+ windDir: Double,
+ center: Pair<Double, Double>,
+ distanceRange: Pair<Double, Double>,
+ defaultDegOffset: Double = 60.0,
+ ): List<Pair<Double, Double>> {
+
+ val sDeg = windDir - defaultDegOffset / 2
+ val eDeg = windDir + defaultDegOffset / 2
+
+ val result = mutableListOf<Pair<Double, Double>>()
+
+ if (distanceRange.first == .0) {
+ result.add(center)
+ } else {
+ // 浠庡紑濮嬭搴﹀惊鐜绠楀潗鏍囩偣鍊肩粨鏉熻搴︼紝姝ラ暱1掳
+ var startDeg = sDeg
+ while (startDeg <= eDeg) {
+ val p = MapUtil.getPointByLen(center, distanceRange.first, startDeg * PI / 180)
+ result.add(p)
+ startDeg++
+ }
+ }
+
+ if (distanceRange.second > .0) {
+ // 姝ゅ闇�瑕佷粠缁撴潫瑙掑害寮�濮嬪弽鍚戝惊鐜绠楄嚦寮�濮嬭搴︼紝姝ラ暱1掳锛屼娇寰椾袱缁勫潗鏍囩偣鎸夐『搴忔帓鍒楋紝鍙粯鍒跺搴旂殑澶氳竟褰�
+ var startDeg = eDeg
+ while (startDeg >= sDeg) {
+ val p = MapUtil.getPointByLen(center, distanceRange.second, startDeg * PI / 180)
+ result.add(p)
+ startDeg--
+ }
+ }
+
+ return result
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt
index 9893656..6775572 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt
@@ -1,10 +1,55 @@
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.biz.sourcetrace.config.RTExcWindLevelConfig
+import com.flightfeather.uav.common.utils.DateUtil
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.SceneInfo
+import com.flightfeather.uav.domain.repository.SceneInfoRep
+
/**
* 姹℃煋绾跨储
- * 鏍规嵁
+ * 閫氳繃姹℃煋鏁版嵁[PollutedData],姹℃煋鍖哄煙[PollutedArea],姹℃煋鏉ユ簮[PollutedSource]锛屽舰鎴愪竴鏉℃薄鏌撴函婧愮嚎绱�
* @date 2025/5/27
* @author feiyu02
*/
-class PollutedClue {
+class PollutedClue() : BaseExceptionResult() {
+
+ constructor(
+ start: BaseRealTimeData,
+ end: BaseRealTimeData?,
+ factor: FactorFilter.SelectedFactor,
+ exceptionData: List<BaseRealTimeData>,
+ eType: ExceptionType,
+ config: RTExcWindLevelConfig,
+ windLevelCondition: RTExcWindLevelConfig.WindLevelCondition
+ ) : this() {
+ this.factor = factor
+ if (exceptionData.isEmpty()) return
+ pollutedData = PollutedData(start, end, factor, exceptionData, eType, windLevelCondition)
+ pollutedArea = PollutedArea(exceptionData, config, windLevelCondition)
+ }
+
+ /**
+ * 6. 灞曠ず鏁版嵁鍙樺寲鎯呭喌锛屼笂鍗囬�熺巼绛夌瓑
+ */
+
+ var pollutedData: PollutedData? = null
+
+ var pollutedArea: PollutedArea? = null
+
+ var pollutedSource: PollutedSource? = null
+
+ private var factor: FactorFilter.SelectedFactor? = null
+
+ /**
+ * 鏌ユ壘绯荤粺鍐呴儴婧簮鑼冨洿鍐呯殑姹℃煋浼佷笟
+ */
+ fun searchScenes(sceneInfoRep: SceneInfoRep) {
+ if (pollutedArea == null || factor == null) return
+ pollutedSource = PollutedSource().also { it.searchScenes(pollutedArea!!, sceneInfoRep, factor!!) }
+ }
}
\ No newline at end of file
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 8ae7734..aa722b8 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
@@ -1,5 +1,9 @@
package com.flightfeather.uav.biz.sourcetrace.model
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.lightshare.bean.DataVo
@@ -8,7 +12,7 @@
* @date 2025/5/27
* @author feiyu02
*/
-class PollutedData {
+class PollutedData() {
/**
*
@@ -20,11 +24,68 @@
* 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鐨勬瘮閲嶅彉鍖栵紝宸ュ湴涓轰富
+ * c) VOC杈冮珮锛屽悓姣旇绠梡m2.5鐨勯噺绾э紝鍙兘瀛樺湪鍚屾鍋忛珮锛堟苯淇�佸姞娌圭珯锛�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
+ * d) VOC杈冮珮锛屽浜庡姞娌圭珯锛堣溅杈嗘嫢鍫垫儏鍐碉級锛孋O涓�鑸緝楂�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
+ * e) 姘哀鍖栧悎鐗╋紝涓�鑸敱浜庢満鍔ㄨ溅灏炬皵锛屽悓姝ヨ绠桟O
+ */
+
+ constructor(
+ start: BaseRealTimeData,
+ end: BaseRealTimeData?,
+ factor: FactorFilter.SelectedFactor,
+ exceptionData: List<BaseRealTimeData>,
+ eType: ExceptionType,
+ windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
+ ) : this() {
+ 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
+
+ windSpeed = exceptionData.first().windSpeed?.toDouble()
+ percentage = windLevelCondition.mutationRate.first
+ times = windLevelCondition.countLimit
+
+ exceptionData.forEach {
+ dataList.add(it)
+ dataVoList.add(it.toDataVo())
+ }
+ }
+
+ 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 windSpeed: Float? = null
+ var windSpeed: Double? = null
// 鍥犲瓙閲忕骇鍙樺寲骞呭害
- var percentage: Float? = null
+ var percentage: Double? = null
// 鍙戠敓娆℃暟
var times: Int? = null
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 d0e3fd0..de0dc62 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
@@ -1,5 +1,10 @@
package com.flightfeather.uav.biz.sourcetrace.model
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.SceneInfo
+import com.flightfeather.uav.domain.repository.SceneInfoRep
+
/**
* 姹℃煋鏉ユ簮
* 绯荤粺鍐呴儴鐨勬薄鏌撳満鏅�佺數瀛愬湴鍥炬悳绱㈠緱鍒扮殑瀹為檯璺璺彛绛夋爣蹇椾俊鎭�
@@ -8,5 +13,38 @@
*/
class PollutedSource {
+ /**
+ * 婧簮娓呭崟鏄剧ず涓庝复杩戠洃娴嬬珯鐐圭殑璺濈锛堝浗鎺с�佸競鎺с�佺綉鏍煎寲鐩戞祴鐐癸級
+ *
+ */
+ // 婧簮浼佷笟
+ var sceneList:List<SceneInfo?>? = null
+
+ /**
+ * 鏌ユ壘绯荤粺鍐呴儴婧簮鑼冨洿鍐呯殑姹℃煋浼佷笟
+ */
+ fun searchScenes(pollutedArea: PollutedArea, sceneInfoRep: SceneInfoRep, factor: FactorFilter.SelectedFactor) {
+ // Fixme 2025.5.14: 姹℃煋婧愮殑鍧愭爣鏄珮寰峰湴鍥惧潗鏍囩郴锛堢伀鏄熷潗鏍囩郴锛夛紝鑰岃蛋鑸暟鎹槸WGS84鍧愭爣绯�
+ // 鎸夌収鍖哄煙妫�绱㈠唴閮ㄦ薄鏌撴簮淇℃伅
+ // 1. 棣栧厛鎸夌収鍥涜嚦鑼冨洿浠庢暟鎹簱鍒濇绛涢�夋薄鏌撴簮锛岄渶瑕佸厛灏嗗潗鏍囪浆鎹负gcj02锛堢伀鏄熷潗鏍囩郴锛夛紝鍥犱负姹℃煋婧愬満鏅俊鎭兘涓烘鍧愭爣绯�
+ val polygonTmp = pollutedArea.polygon!!.map {
+ MapUtil.gcj02ToWgs84(it)
+ }
+ val fb = MapUtil.calFourBoundaries(polygonTmp)
+ val sceneList = sceneInfoRep.findByCoordinateRange(fb)
+ // 2. 鍐嶇簿纭垽鏂槸鍚﹀湪鍙嶅悜婧簮鍖哄煙澶氳竟褰㈠唴閮�
+ val result = mutableListOf<SceneInfo>()
+ sceneList.forEach {
+ val point = it!!.longitude.toDouble() to it.latitude.toDouble()
+ if (MapUtil.isPointInPolygon(point, polygonTmp)) {
+ result.add(it)
+ }
+ }
+
+ this.sceneList = result
+
+ TODO("鎸夌収鎵�閫夌洃娴嬪洜瀛愮被鍨嬶紝鍖哄垎姹℃煋婧愮被鍨�")
+
+ }
}
\ 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 e4eed40..91983a8 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
@@ -2,9 +2,18 @@
/**
* 姹℃煋鎯呭喌姹囨��
- * 閽堝鍗曟璧拌埅锛屽畾鏃剁粺璁″凡鏈夋薄鏌撶嚎绱紝鎸夌収绛栫暐缁欏嚭璧拌埅寤鸿
+ * 閽堝鍗曟璧拌埅锛屽畾鏃剁粺璁″凡鏈夋薄鏌撶嚎绱PollutedClue]锛屾寜鐓х瓥鐣ョ粰鍑鸿蛋鑸缓璁�
* @date 2025/5/27
* @author feiyu02
*/
class PollutedSummary {
+
+
+ /**
+ * 5. 姹℃煋婧愮殑琚壂鎻忔鏁�
+ * 姣忎竴鍒婚挓瀵瑰巻鍙茬嚎绱㈣繘琛岀粺璁★紝鎻愬嚭浼氬晢寤鸿锛堢姹℃煋婧愯緝杩溿�佹薄鏌撴簮鏁伴噺銆佸嚭鐜版鏁帮級銆佽蛋鑸矾绾胯皟鏁村缓璁紙绂绘薄鏌撴簮杈冭繎銆佽蛋鑸建杩规湭鎺ヨ繎婧簮鍦烘櫙锛�
+ */
+
+ // 姹℃煋绾跨储
+ var clueList = mutableListOf<PollutedClue>()
}
\ No newline at end of file
--
Gitblit v1.9.3