From 8fc27dba6719041402e3e3c099e2f3e01d9d52c7 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期三, 16 七月 2025 17:30:56 +0800
Subject: [PATCH] 2025.7.16 1. 修改动态溯源异常判断逻辑
---
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt | 4
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt | 40 +--
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt | 106 +++++----
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt | 129 +++++++-----
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt | 99 ++++++---
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcChangeRate.kt | 24 +
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionTag.kt | 2
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt | 6
src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt | 8
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionDataExceed.kt | 4
src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt | 2
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt | 13 -
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt | 84 ++++++--
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt | 4
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt | 28 +-
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt | 4
16 files changed, 346 insertions(+), 211 deletions(-)
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt b/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt
index 6a34992..b227bc4 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt
@@ -28,6 +28,11 @@
return this
}
+ fun withCombination(com: List<List<FactorType>>):Builder{
+ combination.addAll(com)
+ return this
+ }
+
fun create(): FactorFilter {
return this@FactorFilter
}
@@ -85,6 +90,9 @@
// 鎵�閫夊洜瀛愰泦鍚�
val selectedList = mutableListOf<SelectedFactor>()
+ // 鍥犲瓙鐨勫叧鑱斿叧绯�
+ val combination = mutableListOf<List<FactorType>>()
+
fun mainList(): List<FactorType> {
return selectedList.map { it.main }
}
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt
index df613f2..f1ee8b6 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseAnalysisConfig.kt
@@ -8,14 +8,7 @@
* @date 2025/5/13
* @author feiyu02
*/
-abstract class BaseAnalysisConfig{
- constructor(factorFilter: FactorFilter, combination: List<List<FactorType>>?){
- this.factorFilter = factorFilter
- this.combination = combination
- }
+abstract class BaseAnalysisConfig(
// 鍥犲瓙绛涢��
- constructor(factorFilter: FactorFilter):this(factorFilter, null)
-
- val factorFilter:FactorFilter
- val combination: List<List<FactorType>>?
-}
\ No newline at end of file
+ val factorFilter: FactorFilter,
+)
\ 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 0336e02..733641e 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -5,13 +5,14 @@
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
import com.flightfeather.uav.socket.eunm.FactorType
+import org.springframework.beans.BeanUtils
import java.time.Duration
/**
* 杩炵画绫诲瀷鐨勫紓甯稿垎鏋愬熀绫�,閫傜敤浜庡綋鍓嶆暟鎹笌鐩搁偦鏁版嵁涔嬮棿鏈夊叧鑱斿叧绯荤殑鎯呭喌
*/
abstract class BaseExceptionContinuous<T : ExceptionTag, V : BaseAnalysisConfig, Y : BaseExceptionResult>(
- config: V, private val tagClz: Class<T>
+ config: V, private val tagClz: Class<T>,
) : BaseExceptionAnalysis<V, Y>(config) {
companion object {
@@ -27,11 +28,18 @@
// 鏈熬鏁版嵁瀵硅薄
protected var lastData: BaseRealTimeData? = null
- // 鏈�鏂扮殑涓�缁勫紓甯革紝鏍规嵁璁惧畾鍙傛暟锛屽皢鐩稿叧鑱旂殑鍥犲瓙浜х敓鐨勫紓甯稿悎骞�
- protected val latestExceptionResult = mutableListOf<BaseExceptionResult>()
+ // 鏈�鏂扮殑涓�缁勫紓甯革紝璁板綍鍗曞洜瀛愬紓甯�
+ protected val latestExceptions = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>()
- // 鏈�鏂扮殑涓�缁勫悎骞跺紓甯�
- protected val latestCombinedResult = mutableListOf<List<BaseExceptionResult>>()
+ /**
+ * 鏈�鏂扮殑涓�缁勫悎骞跺紓甯革紝鏍规嵁閰嶇疆鍙傛暟浠嶽latestExceptions]鍗曞洜瀛愬紓甯镐腑锛屽悎骞跺紓甯�
+ */
+ protected val latestCombinedExc = mutableListOf<List<Pair<FactorFilter.SelectedFactor, T>>>()
+
+ /**
+ * 寮傚父缁撴灉
+ */
+ protected val result = mutableListOf<Y>()
/**
* 绔嬪嵆鍒ゆ柇锛氬綋鍑虹幇寮傚父鏃讹紝缂撳瓨寮傚父鏁版嵁鐨勫悓鏃讹紝绔嬪嵆瀵瑰凡鏈夊紓甯歌繘琛屽垽鏂槸鍚︽弧瓒冲紓甯哥粨鏋滆姹�
@@ -148,6 +156,7 @@
lastData = data
mergeExceptionResult()
+ clearExceptions(data)
}
override fun onDone() {
@@ -160,7 +169,7 @@
*/
fun recordException(factor: FactorFilter.SelectedFactor, tag: T, data: BaseRealTimeData) {
checkResult(factor, ExceptionStatusType.Ended)
- tag.refreshWithNextException(data)
+// tag.refreshWithNextException(data)
}
/**
@@ -168,7 +177,7 @@
*/
open fun checkResult(
factor: FactorFilter.SelectedFactor? = null,
- exceptionStatus: ExceptionStatusType = ExceptionStatusType.InProgress
+ exceptionStatus: ExceptionStatusType = ExceptionStatusType.InProgress,
) {
val tag = tagMap[factor?.main]
if (factor != null && tag != null) {
@@ -191,23 +200,24 @@
open fun onNewException(tag: T, factor: FactorFilter.SelectedFactor, exceptionStatus: ExceptionStatusType) {
if (tag.startData == null) return
// val ex = newResult(tag.startData!!, tag.endData, factor, tag.exceptionData)
- val ex = newResult(tag, factor)
- .apply { status = exceptionStatus.value }
- // 寮傚父宸插垱寤烘椂锛屾洿鏂板紓甯镐俊鎭�
- if (tag.exceptionCreated) {
- // 灏嗘渶鏂扮殑寮傚父鐨刧uid璧嬪�肩粰ex
- val lastEx = tag.exceptionResult.last()
- ex.guid = lastEx.guid
- tag.exceptionResult.removeLast()
- tag.exceptionResult.add(ex)
- }
- // 寮傚父鏈垱寤烘椂锛屾柊寤哄紓甯镐俊鎭�
- else {
- tag.exceptionResult.add(ex)
- tag.exceptionCreated = true
- }
-
- latestExceptionResult.add(ex)
+// val ex = newResult(tag, factor)
+// .apply { status = exceptionStatus.value }
+// // 寮傚父宸插垱寤烘椂锛屾洿鏂板紓甯镐俊鎭�
+// if (tag.exceptionCreated) {
+// // 灏嗘渶鏂扮殑寮傚父鐨刧uid璧嬪�肩粰ex
+// val lastEx = tag.exceptionResult.last()
+// ex.guid = lastEx.guid
+// tag.exceptionResult.removeLast()
+// tag.exceptionResult.add(ex)
+// }
+// // 寮傚父鏈垱寤烘椂锛屾柊寤哄紓甯镐俊鎭�
+// else {
+// tag.exceptionResult.add(ex)
+// tag.exceptionCreated = true
+// }
+// val tagClone = tagClz.newInstance()
+// BeanUtils.copyProperties(tag, tagClone)
+ latestExceptions.add(factor to tag)
}
/**
@@ -215,13 +225,13 @@
*/
open fun mergeExceptionResult() {
// 閬嶅巻鎵�鏈夌殑鍥犲瓙缁勫悎
- config.combination?.forEach {c ->
- val res = mutableListOf<BaseExceptionResult>()
+ config.factorFilter.combination.forEach { c ->
+ val res = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>()
var exist = true
// 鏌ョ湅缁勫悎鍐呯殑鎵�鏈夊洜瀛愭槸鍚﹂兘鍚屾椂鍑虹幇寮傚父
- c.forEach { f->
- val r = latestExceptionResult.find { e->
- e.factorId == f.value
+ c.forEach { f ->
+ val r = latestExceptions.find { e ->
+ e.first.main == f
}
if (r != null) {
res.add(r)
@@ -232,18 +242,41 @@
// 濡傛灉缁勫悎鍐呯殑鎵�鏈夊洜瀛愰兘瀛樺湪寮傚父锛屽垯瀛樺偍涓哄悎骞跺紓甯�
if (exist) {
// 灏嗗悎骞跺紓甯镐粠鍗曚釜寮傚父闆嗗悎涓幓闄�
- res.forEach { r->
- latestExceptionResult.removeIf { e-> e.factorId == r.factorId }
+ res.forEach { r ->
+ latestExceptions.removeIf { e -> e.first.main == r.first.main }
}
// 灏嗗悎骞跺紓甯稿瓨鍌�
- latestCombinedResult.add(res)
+ latestCombinedExc.add(res)
}
}
+ // 瀛樺偍寮傚父缁撴灉
+ latestExceptions.forEach {
+ result.add(newResult(listOf(it)))
+ }
+ latestCombinedExc.forEach {
+ result.add(newResult(it))
+ }
+ }
+
+ private fun clearExceptions(data: BaseRealTimeData) {
+ latestExceptions.forEach {
+ it.second.refreshWithNextException(data)
+ }
+ latestExceptions.clear()
+ latestCombinedExc.forEach {
+ it.forEach { e ->
+ e.second.refreshWithNextException(data)
+ }
+ }
+ latestCombinedExc.clear()
+ result.clear()
}
/**
* 鐢熸垚涓�鏉″紓甯稿垎鏋愮粨鏋�
*/
- abstract fun newResult(tag:T, factor: FactorFilter.SelectedFactor): Y
+ abstract fun newResult(tag: T, factor: FactorFilter.SelectedFactor): Y
+
+ abstract fun newResult(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>): Y
}
\ 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
index bbd22fa..ba5fef9 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionContinuous.kt
@@ -20,4 +20,8 @@
return ExceptionResult(tag.startData!!, tag.endData, factor, tag.exceptionData, config.mission.missionCode,
eType)
}
+
+ override fun newResult(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>): ExceptionResult {
+ return ExceptionResult()
+ }
}
\ 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 05265fc..f2db635 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
@@ -42,4 +42,8 @@
return ExceptionResult(tag.startData!!, tag.endData, factor, tag.exceptionData, config.mission.missionCode,
eType)
}
+
+ override fun newResult(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>): ExceptionResult {
+ return ExceptionResult()
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionTag.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionTag.kt
index 3124bc8..15368b2 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionTag.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/ExceptionTag.kt
@@ -37,7 +37,7 @@
fun addHistoryData(data: BaseRealTimeData) {
historyData.add(data)
- if (historyData.size > 20) {
+ if (historyData.size > 15) {
historyData.removeAt(0)
}
}
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt
index d721f59..0e4c5ef 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt
@@ -32,7 +32,7 @@
this.sceneInfoRep = sceneInfoRep
this.sourceTraceRep = sourceTraceRep
this.config = if (factorFilter != null) {
- RTExcWindLevelConfig(factorFilter, emptyList())
+ RTExcWindLevelConfig(factorFilter)
} else {
RTExcWindLevelConfig(
FactorFilter.builder()
@@ -44,11 +44,13 @@
.withMain(FactorType.PM25)
.withMain(FactorType.PM10)
.withMain(FactorType.VOC)
- .create(),
- listOf(
- listOf(FactorType.PM25, FactorType.PM10),
- listOf(FactorType.VOC, FactorType.CO),
- )
+ .withCombination(
+ listOf(
+ listOf(FactorType.PM25, FactorType.PM10),
+ listOf(FactorType.VOC, FactorType.CO),
+ )
+ )
+ .create()
)
}
pollutedSummary = PollutedSummary(config) { summaryCallback(it) }
@@ -110,32 +112,28 @@
}
// 鏁版嵁绐佸彉寮傚父鍥炶皟
- private fun exceptionCallback(ex: List<PollutedClue>) {
- ex.forEach {
- // 婧簮姹℃煋婧愪俊鎭�
- it.searchScenes(sceneInfoRep)
- it.msgType = MsgType.PolClue.value
- }
+ private fun exceptionCallback(ex: PollutedClue) {
+ // 婧簮姹℃煋婧愪俊鎭�
+ ex.searchScenes(sceneInfoRep)
+ ex.msgType = MsgType.PolClue.value
// 骞挎挱姹℃煋婧簮寮傚父缁撴灉
UnderwayWebSocketSender.broadcast(MsgType.PolClue.value, ex)
- sourceTraceRep.insertList(MsgType.PolClue, ex)
+ sourceTraceRep.insert(MsgType.PolClue, ex)
// 璁板綍姹℃煋绾跨储
- pollutedSummary.addClueList(ex)
+ pollutedSummary.addClue(ex)
}
// 鏁版嵁鍙樺寲鎻愰啋鍥炶皟
- private fun dataChangeCallback(ex: List<PollutedClue>) {
- ex.forEach {
- // 婧簮姹℃煋婧愪俊鎭�
- it.searchScenes(sceneInfoRep)
- it.msgType = MsgType.DataChange.value
- }
+ private fun dataChangeCallback(ex: PollutedClue) {
+ // 婧簮姹℃煋婧愪俊鎭�
+ ex.searchScenes(sceneInfoRep)
+ ex.msgType = MsgType.DataChange.value
// 骞挎挱鏁版嵁鍙樺寲鎻愰啋
UnderwayWebSocketSender.broadcast(MsgType.DataChange.value, ex)
- sourceTraceRep.insertList(MsgType.DataChange, ex)
+ sourceTraceRep.insert(MsgType.DataChange, ex)
}
private fun summaryCallback(ex: AnalysisResult) {
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 d6a68a2..e855a98 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
@@ -10,15 +10,15 @@
* @date 2025/5/29
* @author feiyu02
*/
-class RTExcWindLevelConfig(factorFilter: FactorFilter, combination: List<List<FactorType>>?): BaseAnalysisConfig
- (factorFilter, combination) {
+class RTExcWindLevelConfig(factorFilter: FactorFilter): BaseAnalysisConfig(factorFilter) {
inner class WindLevelCondition(
val windSpeed: Pair<Double, Double>,
val mutationRate: Pair<Double, Double>,
val distanceType: DistanceType,
val countLimit: Int,
- )
+ ){
+}
// 闄愬畾璺濈鍐咃紙鍗曚綅锛氱背锛�
var distanceLimit = 3000
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcChangeRate.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcChangeRate.kt
index c3ad456..c0c0cd3 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcChangeRate.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcChangeRate.kt
@@ -110,7 +110,14 @@
}
override fun newResult(tag: ExceptionTag, factor: FactorFilter.SelectedFactor): PollutedClue {
- return PollutedClue(tag, factor, getExceptionType(), config, changeRate[factor.main])
+ return PollutedClue()
+ }
+
+ override fun newResult(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>): PollutedClue {
+ return if (exceptions.isEmpty())
+ PollutedClue()
+ else
+ PollutedClue(exceptions, getExceptionType(), config, changeRate[exceptions[0].first.main])
}
override fun onNewException(
@@ -127,15 +134,16 @@
override fun mergeExceptionResult() {
super.mergeExceptionResult()
- latestExceptionResult
- latestCombinedResult
callback?.let { func ->
- latestExceptionResult.forEach {
- func.invoke(listOf(it as PollutedClue))
+ result.forEach {
+ func.invoke(it)
}
- latestCombinedResult.forEach {
- func.invoke(it as List<PollutedClue>)
- }
+// latestExceptions.forEach {
+// func.invoke(listOf(it as PollutedClue))
+// }
+// latestCombinedExc.forEach {
+// func.invoke(it as List<PollutedClue>)
+// }
}
}
}
\ 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 490c6ac..5fc728c 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
@@ -16,7 +16,7 @@
// 寮傚父鏁版嵁鐢熸垚鍥炶皟绫�
-typealias NewPolluteClueCallback = (ex: List<PollutedClue>) -> Unit
+typealias NewPolluteClueCallback = (ex: PollutedClue) -> Unit
/**
* 涓嶅悓椋庨�熶笅锛屾暟鎹獊鍙樺紓甯稿熀绫�
* @date 2025/5/29
@@ -116,10 +116,17 @@
}
override fun newResult(tag: ExceptionTag, factor: FactorFilter.SelectedFactor): PollutedClue {
- return PollutedClue(tag, factor, getExceptionType(), config, windLevelCondition)
+ return PollutedClue()
}
-// override fun newResult(
+ override fun newResult(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>): PollutedClue {
+ return if (exceptions.isEmpty())
+ PollutedClue()
+ else
+ PollutedClue(exceptions, getExceptionType(), config, windLevelCondition)
+ }
+
+ // override fun newResult(
// start: BaseRealTimeData,
// end: BaseRealTimeData?,
// factor: FactorFilter.SelectedFactor,
@@ -142,15 +149,16 @@
override fun mergeExceptionResult() {
super.mergeExceptionResult()
- latestExceptionResult
- latestCombinedResult
callback?.let { func ->
- latestExceptionResult.forEach {
- func.invoke(listOf(it as PollutedClue))
+ result.forEach {
+ func.invoke(it)
}
- latestCombinedResult.forEach {
- func.invoke(it as List<PollutedClue>)
- }
+// latestExceptions.forEach {
+// func.invoke(listOf(it as PollutedClue))
+// }
+// latestCombinedExc.forEach {
+// func.invoke(it as List<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 962969f..1d7d75e 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
@@ -61,6 +61,10 @@
return RealTimeExceptionResult(tag.startData!!, tag.endData, factor, tag.exceptionData, eType)
}
+ override fun newResult(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>): RealTimeExceptionResult {
+ return RealTimeExceptionResult()
+ }
+
override fun onNewException(tag: T, factor: FactorFilter.SelectedFactor, exceptionStatus: ExceptionStatusType) {
super.onNewException(tag, factor, exceptionStatus)
callback?.let { func ->
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 563466a..cd82f6c 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
@@ -102,7 +102,7 @@
// result.add(center)
var startDeg = 0
while (startDeg <= 360) {
- val p = MapUtil.getPointByLen(center, 50.0, startDeg * PI / 180)
+ val p = MapUtil.getPointByLen(center, distanceRange.second, startDeg * PI / 180)
result.add(p)
startDeg++
}
@@ -135,7 +135,7 @@
val result = mutableListOf<Pair<Double, Double>>()
var startDeg = 0
while (startDeg <= 360) {
- val p = MapUtil.getPointByLen(center, 50.0, startDeg * PI / 180)
+ val p = MapUtil.getPointByLen(center, DistanceType.TYPE1.disRange.second, startDeg * PI / 180)
result.add(p)
startDeg++
}
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 c87812e..19eedc2 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
@@ -18,39 +18,81 @@
* @date 2025/5/27
* @author feiyu02
*/
-class PollutedClue() : BaseExceptionResult(){
+class PollutedClue() : BaseExceptionResult() {
// constructor(
-// start: BaseRealTimeData,
-// end: BaseRealTimeData?,
-// factor: FactorFilter.SelectedFactor,
-// exceptionData: List<BaseRealTimeData>,
-// eType: ExceptionType,
-// config: RTExcWindLevelConfig,
+// tag: ExceptionTag, factor: FactorFilter.SelectedFactor, eType: ExceptionType, config: RTExcWindLevelConfig,
// windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
// ) : this() {
-// if (exceptionData.isEmpty()) return
-// pollutedData = PollutedData(start, end, factor, exceptionData, eType, windLevelCondition)
-// pollutedArea = PollutedArea(exceptionData, config, windLevelCondition)
+// if (tag.exceptionData.isEmpty()) return
+// deviceCode = tag.startData?.deviceCode
+// pollutedData = PollutedData(
+// tag.startData!!, tag.endData, factor, tag.exceptionData, tag.historyData, eType, windLevelCondition
+// )
+// pollutedArea = PollutedArea(tag.historyData, tag.exceptionData, config, windLevelCondition)
// }
constructor(
- tag: ExceptionTag, factor: FactorFilter.SelectedFactor, eType: ExceptionType, config: RTExcWindLevelConfig,
+ exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>,
+ eType: ExceptionType,
+ config: RTExcWindLevelConfig,
windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
- ) : this()
-// this(
-// tag.startData!!, tag.endData, factor, tag.exceptionData, eType, config,
-// windLevelCondition
-// )
- {
- if (tag.exceptionData.isEmpty()) return
- deviceCode = tag.startData?.deviceCode
+ ) : this() {
+ if (exceptions.isEmpty() || exceptions[0].second.exceptionData.isEmpty()) return
+ deviceCode = exceptions[0].second.startData?.deviceCode
+ var startData: BaseRealTimeData? = null
+ var endData: BaseRealTimeData? = null
+ var exceptionData = mutableListOf<BaseRealTimeData>()
+ var historyData = mutableListOf<BaseRealTimeData>()
+ exceptions.forEach { e ->
+ if (startData == null) {
+ startData = e.second.startData
+ } else {
+ if (e.second.startData?.dataTime!! < startData!!.dataTime) {
+ startData = e.second.startData
+ }
+ }
+
+ if (endData == null) {
+ endData = e.second.endData
+ } else {
+ if (e.second.endData?.dataTime!! > endData!!.dataTime) {
+ endData = e.second.endData
+ }
+ }
+
+ if (exceptionData.isEmpty()) {
+ exceptionData = e.second.exceptionData
+ } else {
+ e.second.exceptionData.forEach {
+ if (exceptionData.find { d -> d.dataTime == it.dataTime } == null) {
+ exceptionData.add(it)
+ }
+ }
+ }
+
+ if (historyData.isEmpty()) {
+ historyData = e.second.historyData
+ } else {
+ e.second.historyData.forEach {
+ if (historyData.find { d -> d.dataTime == it.dataTime } == null) {
+ historyData.add(it)
+ }
+ }
+ }
+ }
+ exceptionData.sortBy { it.dataTime }
+ historyData.sortBy { it.dataTime }
+
+ val factorList = exceptions.map { it.first }
pollutedData = PollutedData(
- tag.startData!!, tag.endData, factor, tag.exceptionData, tag.historyData, eType, windLevelCondition
+ startData!!, endData, factorList, exceptionData, historyData, eType, windLevelCondition
)
- pollutedArea = PollutedArea(tag.historyData, tag.exceptionData, config, windLevelCondition)
+ pollutedArea = PollutedArea(historyData, exceptionData, config, windLevelCondition)
+
}
+
/**
* 6. 灞曠ず鏁版嵁鍙樺寲鎯呭喌锛屼笂鍗囬�熺巼绛夌瓑
*/
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 e38d254..3dc2e05 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
@@ -17,6 +17,23 @@
*/
class PollutedData() {
+ inner class Statistic(){
+ var factorId: Int? = null
+ var factorName: String? = null
+ var subFactorId: List<Int>? = null
+ var subFactorName: List<String>? = null
+ var selectedFactor: FactorFilter.SelectedFactor? = null
+
+ // 鍥犲瓙閲忕骇骞冲潎鍙樺寲骞呭害
+ var avgPer: Double? = null
+ // 鍥犲瓙閲忕骇骞冲潎鍙樺寲閫熺巼
+ var avgRate: Double? = null
+
+ var avg: Double? = null
+ var min: Double? = null
+ var max: Double? = null
+ }
+
/**
* 9. 鍏宠仈鍥犲瓙
* a) pm2.5銆乸m10鐗瑰埆楂橈紝涓よ�呭湪鍚勬儏鍐典笅鍚屾灞曠ず锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝姣旈噸瓒婇珮锛岃秺鏈夊彲鑳芥槸椁愰ギ
@@ -29,7 +46,7 @@
constructor(
start: BaseRealTimeData,
end: BaseRealTimeData?,
- factor: FactorFilter.SelectedFactor,
+ factorList: List<FactorFilter.SelectedFactor>,
exceptionData: List<BaseRealTimeData>,
historyData: List<BaseRealTimeData>,
eType: ExceptionType,
@@ -37,11 +54,6 @@
) : 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 = start.dataTime
endTime = end?.dataTime
@@ -51,7 +63,6 @@
endData = end
windSpeed = exceptionData.first().windSpeed?.toDouble()
- percentage = windLevelCondition?.mutationRate?.first
times = windLevelCondition?.countLimit
dataList.add(start)
@@ -61,25 +72,30 @@
dataVoList.addAll(dataList.map { it.toDataVo() })
historyDataList.addAll(historyData.map { it.toDataVo() })
- calPer()
- calRate()
- val s = dataSummary(exceptionData, factor.main)
- avg = s.first
- min = s.second
- max = s.third
+ factorList.forEach { f->
+ statisticMap[f.main] = Statistic().apply {
+ factorId = f.main.value
+ factorName = f.main.des
+ subFactorId = f.subs.map { it.value }
+ subFactorName = f.subs.map { it.des }
+ selectedFactor = f
+
+ avgPer = calPer(f.main)
+ avgRate = calRate(f.main)
+
+ val s = dataSummary(exceptionData, f.main)
+ avg = s.first
+ min = s.second
+ max = s.third
+ }
+ }
}
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: Date? = null
var endTime: Date? = null
@@ -90,17 +106,6 @@
// 椋庨��
var windSpeed: Double? = null
- // 鍥犲瓙閲忕骇鍙樺寲骞呭害
- var percentage: Double? = null
- // 鍥犲瓙閲忕骇骞冲潎鍙樺寲骞呭害
- var avgPer: Double? = null
- // 鍥犲瓙閲忕骇骞冲潎鍙樺寲閫熺巼
- var avgRate: Double? = null
-
- var avg: Double? = null
- var min: Double? = null
- var max: Double? = null
-
// 鍙戠敓娆℃暟
var times: Int? = null
@@ -109,34 +114,37 @@
var dataList: MutableList<BaseRealTimeData> = mutableListOf()
var dataVoList: MutableList<DataVo> = mutableListOf()
- private fun calPer() {
- val list = dataList
-// list.add(startData)
-// list.addAll(dataList)
- if (list.size < 2) return
+ var statisticMap = mutableMapOf<FactorType, Statistic>()
- var total = .0
- for (i in 0 until list.size - 1) {
- val p = list[i]?.getByFactorType(selectedFactor!!.main)!!
- val n = list[i + 1]?.getByFactorType(selectedFactor!!.main)!!
- total += (n - p) / p
- }
- avgPer = total / (list.size - 1)
+ fun toFactorNames(): String {
+ val factors = statisticMap.entries.map { it.key }.sortedBy { it.value }.joinToString(";") { it.des }
+ return factors
}
- private fun calRate() {
+ private fun calPer(factorType: FactorType): Double? {
val list = dataList
-// list.add(startData)
-// list.addAll(dataList)
- if (list.size < 2) return
+ if (list.size < 2) return null
var total = .0
for (i in 0 until list.size - 1) {
- val p = list[i]?.getByFactorType(selectedFactor!!.main)!!
- val n = list[i + 1]?.getByFactorType(selectedFactor!!.main)!!
+ val p = list[i].getByFactorType(factorType)!!
+ val n = list[i + 1].getByFactorType(factorType)!!
+ total += (n - p) / p
+ }
+ return total / (list.size - 1)
+ }
+
+ private fun calRate(factorType: FactorType): Double? {
+ val list = dataList
+ if (list.size < 2) return null
+
+ var total = .0
+ for (i in 0 until list.size - 1) {
+ val p = list[i].getByFactorType(factorType)!!
+ val n = list[i + 1].getByFactorType(factorType)!!
total += (n - p) / 4
}
- avgRate = total / (list.size - 1)
+ return total / (list.size - 1)
}
private fun dataSummary(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Triple<Double, Double,
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 3e52986..6d7b8f0 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
@@ -90,57 +90,82 @@
*/
@Throws(Exception::class)
private fun calSceneType(pollutedData: PollutedData): Pair<String, List<SceneType>>? {
- when (pollutedData.selectedFactor?.main) {
- // 姘哀鍖栧悎鐗╋紝涓�鑸敱浜庢満鍔ㄨ溅灏炬皵锛屽悓姝ヨ绠桟O
- FactorType.NO2 -> {
- val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
- return "姘哀鍖栧悎鐗╁亸楂橈紝CO鐨勯噺绾т负${coAvg}mg/m鲁锛屼竴鑸敱浜庢満鍔ㄨ溅灏炬皵閫犳垚锛屾薄鏌撴簮浠ユ苯淇�佸姞娌圭珯涓轰富" to
- listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
- }
-
- FactorType.CO -> return null
-
- FactorType.H2S -> return null
-
- FactorType.SO2 -> return null
-
- FactorType.O3 -> return null
- // a) pm2.5銆乸m10鐗瑰埆楂橈紝涓よ�呭湪鍚勬儏鍐典笅鍚屾灞曠ず锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝姣旈噸瓒婇珮锛岃秺鏈夊彲鑳芥槸椁愰ギ
- // b) pm10鐗瑰埆楂樸�乸m2.5杈冮珮锛屽ぇ棰楃矑鎵皹姹℃煋锛屽彧灞曠ずpm10锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝宸ュ湴涓轰富
- FactorType.PM25,
- FactorType.PM10,
- -> {
- val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
- val pm10Avg = round(pollutedData.dataList.map { it.pm10!! }.average() * 10) / 10
- // 璁$畻寮傚父鏁版嵁鐨刾m2.5鍗爌m10姣旈噸鐨勫潎鍊�
- val percentageAvg = pollutedData.dataList.map {
- it.pm25!! / it.pm10!!
- }.average()
- val str =
- "PM2.5閲忕骇涓�${pm25Avg}渭g/m鲁锛孭M10閲忕骇涓�${pm10Avg}渭g/m鲁锛孭M2.5鍗燩M10鐨勬瘮閲嶄负${round(percentageAvg * 100)}%"
- return if (percentageAvg > 0.666) {
- "${str}锛屾瘮閲嶈緝澶э紝姹℃煋婧愪互椁愰ギ涓轰富锛屽伐鍦版涔�" to
- listOf(SceneType.TYPE1, SceneType.TYPE2, SceneType.TYPE3, SceneType.TYPE14, SceneType.TYPE5)
- } else if (percentageAvg < 0.333) {
- "${str}锛屾瘮閲嶈緝灏忥紝灞炰簬澶ч绮掓壃灏樻薄鏌擄紝姹℃煋婧愪互宸ュ湴涓轰富" to
- listOf(SceneType.TYPE1, SceneType.TYPE2, SceneType.TYPE3, SceneType.TYPE14, SceneType.TYPE5)
- } else {
- "${str}锛屾薄鏌撴簮浠ラ楗�佸伐鍦颁负涓�" to
- listOf(SceneType.TYPE1, SceneType.TYPE2, SceneType.TYPE3, SceneType.TYPE14, SceneType.TYPE5)
+ var des: String? = null
+ val sceneTypes = mutableListOf<SceneType>()
+ pollutedData.statisticMap.entries.forEach { s ->
+ val res = when (s.key) {
+ // 姘哀鍖栧悎鐗╋紝涓�鑸敱浜庢満鍔ㄨ溅灏炬皵锛屽悓姝ヨ绠桟O
+ FactorType.NO2 -> {
+ val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
+ "姘哀鍖栧悎鐗╁亸楂橈紝CO鐨勯噺绾т负${coAvg}mg/m鲁锛屼竴鑸敱浜庢満鍔ㄨ溅灏炬皵閫犳垚锛屾薄鏌撴簮浠ユ苯淇�佸姞娌圭珯涓轰富" to
+ listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
}
- }
- // c) VOC杈冮珮锛屽悓姣旇绠梡m2.5鐨勯噺绾э紝鍙兘瀛樺湪鍚屾鍋忛珮锛堟苯淇�佸姞娌圭珯锛�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
- // d) VOC杈冮珮锛屽浜庡姞娌圭珯锛堣溅杈嗘嫢鍫垫儏鍐碉級锛孋O涓�鑸緝楂�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
- FactorType.VOC -> {
- val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
- val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
- val o3Avg = round(pollutedData.dataList.map { it.o3!! }.average() * 10) / 10
- return "VOC鍋忛珮锛屽悓鏃禤M2.5閲忕骇涓�${pm25Avg}渭g/m鲁锛孋O閲忕骇涓�${coAvg}mg/m鲁锛孫3閲忕骇涓�${o3Avg}渭g/m鲁锛屾薄鏌撴簮浠ユ苯淇�佸姞娌圭珯涓轰富" to
- listOf(SceneType.TYPE6, SceneType.TYPE17, SceneType.TYPE12)
- }
- else -> return null
+ FactorType.CO -> null
+
+ FactorType.H2S -> null
+
+ FactorType.SO2 -> null
+
+ FactorType.O3 -> null
+ // a) pm2.5銆乸m10鐗瑰埆楂橈紝涓よ�呭湪鍚勬儏鍐典笅鍚屾灞曠ず锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝姣旈噸瓒婇珮锛岃秺鏈夊彲鑳芥槸椁愰ギ
+ // b) pm10鐗瑰埆楂樸�乸m2.5杈冮珮锛屽ぇ棰楃矑鎵皹姹℃煋锛屽彧灞曠ずpm10锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝宸ュ湴涓轰富
+ FactorType.PM25,
+ FactorType.PM10,
+ -> {
+ val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
+ val pm10Avg = round(pollutedData.dataList.map { it.pm10!! }.average() * 10) / 10
+ // 璁$畻寮傚父鏁版嵁鐨刾m2.5鍗爌m10姣旈噸鐨勫潎鍊�
+ val percentageAvg = pollutedData.dataList.map {
+ it.pm25!! / it.pm10!!
+ }.average()
+ val str =
+ "PM2.5閲忕骇涓�${pm25Avg}渭g/m鲁锛孭M10閲忕骇涓�${pm10Avg}渭g/m鲁锛孭M2.5鍗燩M10鐨勬瘮閲嶄负${round(percentageAvg * 100)}%"
+ if (percentageAvg > 0.666) {
+ "${str}锛屾瘮閲嶈緝澶э紝姹℃煋婧愪互椁愰ギ涓轰富锛屽伐鍦版涔�" to
+ listOf(
+ SceneType.TYPE1,
+ SceneType.TYPE2,
+ SceneType.TYPE3,
+ SceneType.TYPE14,
+ SceneType.TYPE5
+ )
+ } else if (percentageAvg < 0.333) {
+ "${str}锛屾瘮閲嶈緝灏忥紝灞炰簬澶ч绮掓壃灏樻薄鏌擄紝姹℃煋婧愪互宸ュ湴涓轰富" to
+ listOf(
+ SceneType.TYPE1,
+ SceneType.TYPE2,
+ SceneType.TYPE3,
+ SceneType.TYPE14,
+ SceneType.TYPE5
+ )
+ } else {
+ "${str}锛屾薄鏌撴簮浠ラ楗�佸伐鍦颁负涓�" to
+ listOf(
+ SceneType.TYPE1,
+ SceneType.TYPE2,
+ SceneType.TYPE3,
+ SceneType.TYPE14,
+ SceneType.TYPE5
+ )
+ }
+ }
+ // c) VOC杈冮珮锛屽悓姣旇绠梡m2.5鐨勯噺绾э紝鍙兘瀛樺湪鍚屾鍋忛珮锛堟苯淇�佸姞娌圭珯锛�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
+ // d) VOC杈冮珮锛屽浜庡姞娌圭珯锛堣溅杈嗘嫢鍫垫儏鍐碉級锛孋O涓�鑸緝楂�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
+ FactorType.VOC -> {
+ val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
+ val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
+ val o3Avg = round(pollutedData.dataList.map { it.o3!! }.average() * 10) / 10
+ "VOC鍋忛珮锛屽悓鏃禤M2.5閲忕骇涓�${pm25Avg}渭g/m鲁锛孋O閲忕骇涓�${coAvg}mg/m鲁锛孫3閲忕骇涓�${o3Avg}渭g/m鲁锛屾薄鏌撴簮浠ユ苯淇�佸姞娌圭珯涓轰富" to
+ listOf(SceneType.TYPE6, SceneType.TYPE17, SceneType.TYPE12)
+ }
+
+ else -> null
+ }
+ des = res?.first
+ res?.second?.let { sceneTypes.addAll(it) }
}
+ return (des ?: "") to sceneTypes
}
/**
@@ -186,10 +211,10 @@
val st = DateUtil.instance.getTime(pollutedData.startTime)
val et = DateUtil.instance.getTime(pollutedData.endTime)
var txt =
- "${pollutedData.selectedFactor?.main?.des}鍦�${st}鑷�${et}涔嬮棿锛屽嚭鐜�${pollutedData.exception}锛屾渶浣庡�间负${
- pollutedData
- .min
- }锛屾渶楂樺�间负${pollutedData.max}"
+ "鍦�${st}鑷�${et}涔嬮棿锛屽嚭鐜�${pollutedData.exception}"
+ pollutedData.statisticMap.entries.forEach {s ->
+ txt += "锛�${s.key.des}鏈�浣庡�间负${s.value.min}渭g/m鲁锛屾渶楂樺�间负${s.value.max}渭g/m鲁锛屽潎鍊间负${s.value.avg}渭g/m鲁"
+ }
if (sceneList.isEmpty()) {
txt += ("锛屽彲鑳藉瓨鍦ㄩ殣钘忛闄╂簮銆�")
} else {
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt b/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt
index 5b961ba..b4f945a 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt
@@ -27,7 +27,7 @@
fun insert(msgType: MsgType, obj: PollutedClue): Int {
val stm = SourceTraceMsg().apply {
deviceCode = obj.deviceCode
- factorName = obj.pollutedData?.factorName
+ factorName = obj.pollutedData?.toFactorNames()
exceptionType = obj.pollutedData?.exceptionType
startTime = obj.pollutedData?.startTime
endTime = obj.pollutedData?.endTime
--
Gitblit v1.9.3