2025.8.14
1. 动态溯源模块添加滑动平均异常计算(调试中)
| | |
| | | lastData = data |
| | | |
| | | removeSingleFactor(data) |
| | | checkDelayedExceptions(data) |
| | | mergeExceptionResult(data) |
| | | val fittedComb = checkDelayedExceptions(data) |
| | | mergeExceptionResult(data, fittedComb) |
| | | onNewResult(result) |
| | | clearExceptions(data) |
| | | } |
| | |
| | | * å°ä¸å¨å
³èå
³ç³»ä¸ççæµå åå¼å¸¸åå¨ï¼å¹¶åé¤ |
| | | */ |
| | | fun removeSingleFactor(data: BaseRealTimeData) { |
| | | if (latestExceptions.isEmpty()) return |
| | | |
| | | // æ¥æ¾ä¸å¨å åå
³èç»åä¸çå¼å¸¸å å |
| | | val sfList = latestExceptions.filter { |
| | | config.factorFilter.combination.find { c -> c.find { f -> f == it.first.main } != null } == null |
| | |
| | | /** |
| | | * æ£æ¥å»¶è¿çå¾
åå¹¶å¼å¸¸ä¸å½åå¼å¸¸æ¯å¦è½å¹é
|
| | | * 1. å°éççè¶
è¿çå¾
æ°æ®å¨æçå¼å¸¸åå¨ |
| | | * 2. å°å¹é
æåçåå¹¶å¼å¸¸åå¨ |
| | | * 2. å°å¹é
æåçåå¹¶å¼å¸¸åå¨ï¼åæ¶å°å
³èå
³ç³»æ 记为已å¹é
|
| | | * 3. ä¿ç便§æªåå¹¶æåå¹¶ä¸å¯ç»§ç»çå¾
çå¼å¸¸ |
| | | * @return 被å¹é
æåçå
³èå
³ç³» |
| | | */ |
| | | fun checkDelayedExceptions(data: BaseRealTimeData): List<List<FactorType>> { |
| | | if (latestExceptions.isEmpty()) return emptyList() |
| | | |
| | | // 被å¹é
æåççæµå åå
³èå
³ç³» |
| | | val fittedComb = mutableListOf<List<FactorType>>() |
| | | // éççè¿å
¥ä¸ä¸ä¸ªæ°æ®å¨æå夿çå¾
åå¹¶å¼å¸¸éå |
| | |
| | | // æ¬æ¬¡æ°æ®å¨æä¸ï¼è¢«å¹é
æåçå¼å¸¸éå |
| | | val exceps = mutableListOf<Pair<FactorFilter.SelectedFactor, T>>() |
| | | remainingExceptions.forEach { |
| | | // æ£æ¥å½åæ°å¼å¸¸ä¸ï¼æ¯å¦å
å«å åå
³èå
³ç³»ä¸çå¼å¸¸ |
| | | // æ£æ¥æ¬æ¬¡æ°æ®å¨æçå¼å¸¸ä¸ï¼æ¯å¦å
å«å åå
³èå
³ç³»ä¸çå¼å¸¸ |
| | | val combRes = matchCombFactor(it.combination, latestExceptions) |
| | | val res = combRes.second |
| | | // å¤ææ¬æ¬¡æ°æ®å¨æä¸æ¾å°çå ååå·²æçå 忝妿»¡è¶³å
³èå
³ç³» |
| | | val findFactors = mutableListOf<FactorType>() |
| | | res.forEach {r -> findFactors.add(r.first.main) } |
| | | it.exceptions.forEach {r -> findFactors.add(r.first.main) } |
| | | // 夿æ¯å¦è¿æç¼ºå¤±å¼å¸¸ |
| | | val isFitAll = findFactors.distinct() == it.combination |
| | | // å¦æå·²ç»æ²¡æç¼ºå¤±çå¼å¸¸å åï¼åå¯å并为ç»åå¼å¸¸ |
| | | if (isFitAll) { |
| | |
| | | |
| | | /** |
| | | * åå¹¶å¼å¸¸ |
| | | * @param data å½åçæµæ°æ® |
| | | * @param fittedComb å¨éççå¼å¸¸[remainingExceptions]夿ä¸ï¼å·²ç»è¿è¡å¹é
夿çå
³èå
³ç³»ï¼å°ä¸åè¿è¡å¹é
|
| | | */ |
| | | open fun mergeExceptionResult(data: BaseRealTimeData) { |
| | | open fun mergeExceptionResult(data: BaseRealTimeData, fittedComb: List<List<FactorType>>) { |
| | | if (latestExceptions.isEmpty()) return |
| | | |
| | | val combinedExc = mutableListOf<List<Pair<FactorFilter.SelectedFactor, T>>>() |
| | | // éåææçå åç»å |
| | | config.factorFilter.combination.forEach { c -> |
| | | /** |
| | | * è·³è¿å·²ç»å¨[checkDelayedExceptions]ä¸å¤æè¿çå
³èå
³ç³» |
| | | */ |
| | | if (fittedComb.indexOf(c) >= 0) return@forEach |
| | | |
| | | val combRes = matchCombFactor(c, latestExceptions) |
| | | val res = combRes.second |
| | | val exist = combRes.first |
| | |
| | | } |
| | | // å¦åå°å¼å¸¸çæ·±æ·è´çæ¬åå
¥å¾
åå¹¶å¼å¸¸éå |
| | | // TODO 2025.8.4: åç»æ·»å å½å
³èççæµå å累计å¼å¸¸è®¡æ°æ¥è¿é弿¶ï¼æåå
¥éåçé»è¾ |
| | | else { |
| | | else if (res.isNotEmpty()) { |
| | | remainingExceptions.add(RemainException(res, c)) |
| | | } |
| | | } |
| | |
| | | package com.flightfeather.uav.biz.dataanalysis |
| | | |
| | | import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType |
| | | import java.io.Serializable |
| | | import java.util.* |
| | | |
| | | /** |
| | |
| | | * @date 2025/5/13 |
| | | * @author feiyu02 |
| | | */ |
| | | abstract class BaseExceptionResult { |
| | | abstract class BaseExceptionResult : Serializable { |
| | | // å¼å¸¸ç¼å· |
| | | var guid: String? = null |
| | | |
| | |
| | | // ä¿è¯å岿°æ®å
嫿æå¼å¸¸æ°æ®ï¼å¼å¸¸æ°æ®å¯è½ä¸è¿ç»ï¼ï¼å¹¶ä¸å¨é¦ä¸ªå¼å¸¸æ°æ®ä¹åæå¤åä¿å10ä¸ªæ°æ® |
| | | val i = historyData.indexOf(exceptionData.first()) |
| | | if (i > MAX_HISTORY) { |
| | | historyData = historyData.subList(i - MAX_HISTORY, historyData.size) |
| | | historyData = historyData.subList(i - MAX_HISTORY, historyData.size).toMutableList() |
| | | } |
| | | } else { |
| | | if (historyData.size > MAX_HISTORY) { |
| | | historyData = historyData.subList(historyData.size - MAX_HISTORY, historyData.size) |
| | | historyData = historyData.subList(historyData.size - MAX_HISTORY, historyData.size).toMutableList() |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | private fun newTask() { |
| | | taskList.apply { |
| | | // add(RTExcSlideAverage(config) { dataChangeCallback(it) }.also { it.init() }) |
| | | 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() }) |
| | |
| | | 1.6 to 7.9, |
| | | 0.2 to Double.MAX_VALUE, |
| | | DistanceType.TYPE3, |
| | | 3 |
| | | 2 |
| | | // 1 |
| | | ) |
| | | |
| | |
| | | 8.0 to 13.8, |
| | | 0.1 to Double.MAX_VALUE, |
| | | DistanceType.TYPE4, |
| | | 3 |
| | | 2 |
| | | ) |
| | | |
| | | /****æ°æ®å¿«éä¸å*****************************************************************************/ |
| | |
| | | 1.6 to 7.9, |
| | | 4.0 to Double.MAX_VALUE, |
| | | DistanceType.TYPE3, |
| | | 3 |
| | | 2 |
| | | ), |
| | | // PM10å¨ä¸ä¸ªçæµå¨æï¼4ç§ï¼ä¸åé级大äºçäº4μg/m³ï¼è¿ç»åç3次 |
| | | FactorType.PM10 to WindLevelCondition( |
| | | 1.6 to 7.9, |
| | | 4.0 to Double.MAX_VALUE, |
| | | DistanceType.TYPE3, |
| | | 3 |
| | | 2 |
| | | ), |
| | | // VOCå¨ä¸ä¸ªçæµå¨æï¼4ç§ï¼ä¸åé级大äºçäº6μg/m³ï¼è¿ç»åç1次 |
| | | FactorType.VOC to WindLevelCondition( |
| | |
| | | 8.0 to 13.8, |
| | | 4.0 to Double.MAX_VALUE, |
| | | DistanceType.TYPE4, |
| | | 3 |
| | | 2 |
| | | ), |
| | | // PM10å¨ä¸ä¸ªçæµå¨æï¼4ç§ï¼ä¸åé级大äºçäº4μg/m³ï¼è¿ç»åç3次 |
| | | FactorType.PM10 to WindLevelCondition( |
| | | 8.0 to 13.8, |
| | | 4.0 to Double.MAX_VALUE, |
| | | DistanceType.TYPE4, |
| | | 3 |
| | | 2 |
| | | ), |
| | | // VOCå¨ä¸ä¸ªçæµå¨æï¼4ç§ï¼ä¸åé级大äºçäº6μg/m³ï¼è¿ç»åç1次 |
| | | FactorType.VOC to WindLevelCondition( |
| | |
| | | .0 to Double.MAX_VALUE, |
| | | 2.0 to 4.0, |
| | | DistanceType.TYPE1, |
| | | 3 |
| | | 2 |
| | | ), |
| | | // PM10å¨ä¸ä¸ªçæµå¨æï¼4ç§ï¼ä¸åé级å¨2 - 4μg/m³ä¹é´ï¼è¿ç»åç3次 |
| | | FactorType.PM10 to WindLevelCondition( |
| | | .0 to Double.MAX_VALUE, |
| | | 2.0 to 4.0, |
| | | DistanceType.TYPE1, |
| | | 3 |
| | | 2 |
| | | ), |
| | | // VOCå¨ä¸ä¸ªçæµå¨æï¼4ç§ï¼ä¸åé级å¨3 - 6μg/m³ä¹é´ï¼è¿ç»åç2次 |
| | | FactorType.VOC to WindLevelCondition( |
| | |
| | | 2 |
| | | ), |
| | | ) |
| | | |
| | | /****æ»å¨å¹³åå¼å¼å¸¸*****************************************************************************/ |
| | | // æ±æ»å¨å¹³åå¼çæ°æ®ç»ä¸ªæ° |
| | | var changeTrendGroup = 12 |
| | | // æ»å¨å¹³åå¼è¿ç» |
| | | var changeTrendInterval = 12 |
| | | var changeTrendRate = .2 |
| | | // æ»å¨å¹³åå¼ååçå¼å¸¸è¿ç»æ¬¡æ° |
| | | var changeTrendTimes = 3 |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.flightfeather.uav.biz.sourcetrace.exceptiontype |
| | | |
| | | import com.flightfeather.uav.biz.FactorFilter |
| | | import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis |
| | | import com.flightfeather.uav.biz.dataanalysis.exceptiontype.ExceptionSlideAverage.Tag |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionSlideAverageTag |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType |
| | | import com.flightfeather.uav.biz.sourcetrace.RealTimeAnalysisConfig |
| | | import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig |
| | | import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue |
| | | import com.flightfeather.uav.biz.sourcetrace.model.RealTimeExceptionResult |
| | | import com.flightfeather.uav.domain.entity.BaseRealTimeData |
| | | import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType |
| | | import com.flightfeather.uav.socket.eunm.FactorType |
| | | import kotlin.math.abs |
| | | |
| | | /** |
| | | * æ»å¨å¹³åå¼çªåå¼å¸¸ |
| | | * @date 2025/5/13 |
| | | * @author feiyu02 |
| | | */ |
| | | class RTExcSlideAverage : BaseExceptionAnalysis<RTExcWindLevelConfig, PollutedClue> { |
| | | |
| | | constructor(config: RTExcWindLevelConfig) : super(config) |
| | | |
| | | constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config){ |
| | | this.callback = callback |
| | | } |
| | | |
| | | private var callback: NewPolluteClueCallback? = null |
| | | |
| | | private val historyDataList = mutableListOf<BaseRealTimeData>() |
| | | private val tempDataList = mutableListOf<BaseRealTimeData>() |
| | | private var lastData: BaseRealTimeData? = null |
| | | |
| | | protected val tagMap = mutableMapOf<FactorType, ExceptionSlideAverageTag>() |
| | | |
| | | override fun init() { |
| | | super.init() |
| | | historyDataList.clear() |
| | | tempDataList.clear() |
| | | lastData = null |
| | | |
| | | tagMap.clear() |
| | | config.factorFilter.mainList().forEach { f -> |
| | | tagMap[f] = ExceptionSlideAverageTag() |
| | | } |
| | | } |
| | | |
| | | override fun getExceptionType(): ExceptionType { |
| | | return ExceptionType.TYPE7 |
| | | } |
| | | |
| | | override fun onNextData(data: BaseRealTimeData) { |
| | | historyDataList.add(data) |
| | | // æ°æ®å å
¥ä¸´æ¶æ°ç» |
| | | tempDataList.add(data) |
| | | // æ°æ®éè¶
åºè®¾ç½®æ°éæ¶ï¼å»é¤å½åæ°æ®ç»é¦ä¸ªæ°æ® |
| | | if (tempDataList.size > config.changeTrendGroup) { |
| | | tempDataList.removeAt(0) |
| | | } |
| | | config.factorFilter.selectedList.forEach { s -> |
| | | val f = s.main |
| | | tagMap[f]?.let { |
| | | it.eIndex++ |
| | | it.endData = lastData |
| | | if (it.startData == null) { |
| | | it.startData = data |
| | | } |
| | | // æ°æ®éçäºè®¾ç½®æ°éæ¶ï¼è®¡ç®å½åæ°æ®ç»åå¼ |
| | | if (tempDataList.size == config.changeTrendGroup) { |
| | | calAvg(f, tempDataList) |
| | | if (checkSlideAvg(f)) { |
| | | it.addExceptionData(data) |
| | | checkResult(s) |
| | | } else { |
| | | recordException(s, it, data) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | lastData = data |
| | | } |
| | | |
| | | override fun onDone() { |
| | | checkResult(exceptionStatus = ExceptionStatusType.Ended) |
| | | } |
| | | |
| | | /** |
| | | * å¼å¸¸ç»æï¼è®°å½å¼å¸¸ |
| | | */ |
| | | fun recordException(factor: FactorFilter.SelectedFactor, tag: ExceptionSlideAverageTag, data: BaseRealTimeData) { |
| | | checkResult(factor, ExceptionStatusType.Ended) |
| | | tag.refreshAfterCheckResult(historyDataList, config.changeTrendGroup) |
| | | } |
| | | |
| | | /** |
| | | * å½åæ°æ®æªåºç°å¼å¸¸æ¶ï¼ææ°æ®å¾ªç¯ç»ææ¶ï¼å¤æåç»æ¥éª¤ |
| | | */ |
| | | private fun checkResult( |
| | | factor: FactorFilter.SelectedFactor? = null, |
| | | exceptionStatus: ExceptionStatusType = ExceptionStatusType.InProgress |
| | | ) { |
| | | val tag = tagMap[factor?.main] |
| | | if (factor != null && tag != null) { |
| | | if (tag.exceptionExisted) { |
| | | onNewException(tag, factor, exceptionStatus) |
| | | } |
| | | } else { |
| | | config.factorFilter.selectedList.forEach { f -> |
| | | val tag1 = tagMap[f.main] ?: return@forEach |
| | | if (tag1.exceptionExisted) { |
| | | onNewException(tag1, f, exceptionStatus) |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * æ°å¢ææ´æ°ä¸æ¡å¼å¸¸ |
| | | */ |
| | | open fun onNewException(tag: ExceptionSlideAverageTag, factor: FactorFilter.SelectedFactor, exceptionStatus: ExceptionStatusType) { |
| | | if (tag.startData == null) return |
| | | val ex = newResult(listOf(factor to tag)) |
| | | |
| | | callback?.invoke(ex) |
| | | } |
| | | |
| | | fun newResult(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>): PollutedClue { |
| | | return PollutedClue(exceptions, getExceptionType(), config, null) |
| | | } |
| | | |
| | | /** |
| | | * 计ç®ä¸ç»æ°æ®çåå¼ |
| | | */ |
| | | private fun calAvg(type: FactorType, list: List<BaseRealTimeData>) { |
| | | var total = .0 |
| | | var valid = true |
| | | val count = list.size |
| | | if (count == 0) return |
| | | list.forEach { |
| | | val v = it.getByFactorType(type) |
| | | if (v == null) { |
| | | valid = false |
| | | } else { |
| | | total += v |
| | | } |
| | | } |
| | | val avg = total / count |
| | | tagMap[type]?.avgListReverse?.add(0, Pair(avg, valid)) |
| | | } |
| | | |
| | | /** |
| | | * è®¡ç®æ°æ®ç»ä¹é´çåå¼å·®å¼æ¯å¦è¿ç»è¶
è¿é宿¯ç |
| | | */ |
| | | private fun checkSlideAvg(type: FactorType): Boolean { |
| | | val tag = tagMap[type] ?: return false |
| | | // è®¡ç®æ»å¨å弿ä½è¦æ±ä¸ªæ° |
| | | val minSize = config.changeTrendTimes + config.changeTrendInterval |
| | | if (tag.avgListReverse.size < minSize) { |
| | | return false |
| | | } else { |
| | | // æ»å¨å弿»¡è¶³æ°éæ¶ï¼è®¡ç®åå¼ä¹é´æ¯å¦è¿ç»è¶
è¿é宿¯ç |
| | | val rateList = mutableListOf<Pair<Double, Boolean>>() |
| | | for (i in tag.avgListReverse.indices) { |
| | | if (i >= config.changeTrendTimes) break |
| | | val r = calAvgChangeRate(tag.avgListReverse[i], tag.avgListReverse[i + config.changeTrendInterval]) |
| | | rateList.add(r) |
| | | } |
| | | for (y in rateList) { |
| | | if (!y.second || y.first < config.changeTrendRate) { |
| | | return false |
| | | } |
| | | } |
| | | return true |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è®¡ç®æ»å¨åå¼ååç |
| | | * æ±a1ç¸å¯¹äºa2çååç |
| | | */ |
| | | private fun calAvgChangeRate(a1: Pair<Double, Boolean>, a2: Pair<Double, Boolean>): Pair<Double, Boolean> { |
| | | val valid = a1.second && a2.second |
| | | return if (a2.first == .0) { |
| | | Pair(1.0, valid) |
| | | } else { |
| | | Pair(abs(a1.first - a2.first) / a2.first, valid) |
| | | } |
| | | } |
| | | } |
| | |
| | | // æç
§åºåæ£ç´¢å
鍿±¡ææºä¿¡æ¯ |
| | | var result = mutableListOf<SceneInfo>() |
| | | // 1. é¦å
æç
§åè³èå´ä»æ°æ®åºåæ¥çéæ±¡ææºï¼æ¤å¤çåºååæ å·²è½¬æ¢ä¸ºç«æåæ ç³» |
| | | val polygonTmp = pollutedArea.polygon!! |
| | | val fb = MapUtil.calFourBoundaries(polygonTmp) |
| | | val sceneList = sceneInfoRep.findByCoordinateRange(fb) |
| | | // 2. åç²¾ç¡®å¤ææ¯å¦å¨ååæº¯æºåºåå¤è¾¹å½¢å
é¨ |
| | | sceneList.forEach { |
| | | val point = it!!.longitude.toDouble() to it.latitude.toDouble() |
| | | if (MapUtil.isPointInPolygon(point, polygonTmp)) { |
| | | result.add(it) |
| | | } |
| | | } |
| | | val polygonTmp = pollutedArea.polygon |
| | | this.sceneList = emptyList() |
| | | |
| | | val closePolygonTmp = pollutedArea.closePolygon!! |
| | | val closeFb = MapUtil.calFourBoundaries(closePolygonTmp) |
| | | val closeSceneList = sceneInfoRep.findByCoordinateRange(closeFb) |
| | | // 2. åç²¾ç¡®å¤ææ¯å¦å¨ååæº¯æºåºåå¤è¾¹å½¢å
é¨ |
| | | closeSceneList.forEach { |
| | | val point = it!!.longitude.toDouble() to it.latitude.toDouble() |
| | | if (MapUtil.isPointInPolygon(point, closePolygonTmp)) { |
| | | result.add(it) |
| | | } |
| | | } |
| | | |
| | | // æ ¹æ®æ±¡æå åçé级ï¼è®¡ç®ä¸»è¦ç污æåºæ¯ç±»åï¼çéç»æ |
| | | val mainSceneType = calSceneType(pollutedData) |
| | | if (mainSceneType != null) { |
| | | // this.conclusion = mainSceneType.first |
| | | result = result.filter { |
| | | val r = mainSceneType.second.find { s-> |
| | | s.value == it.typeId.toInt() |
| | | if (polygonTmp != null) { |
| | | val fb = MapUtil.calFourBoundaries(polygonTmp) |
| | | val sceneList = sceneInfoRep.findByCoordinateRange(fb) |
| | | // 2. åç²¾ç¡®å¤ææ¯å¦å¨ååæº¯æºåºåå¤è¾¹å½¢å
é¨ |
| | | sceneList.forEach { |
| | | val point = it!!.longitude.toDouble() to it.latitude.toDouble() |
| | | if (MapUtil.isPointInPolygon(point, polygonTmp)) { |
| | | result.add(it) |
| | | } |
| | | r != null |
| | | }.toMutableList() |
| | | } |
| | | } |
| | | |
| | | this.sceneList = findClosestStation(sceneInfoRep, result) |
| | | val closePolygonTmp = pollutedArea.closePolygon!! |
| | | val closeFb = MapUtil.calFourBoundaries(closePolygonTmp) |
| | | val closeSceneList = sceneInfoRep.findByCoordinateRange(closeFb) |
| | | // 2. åç²¾ç¡®å¤ææ¯å¦å¨ååæº¯æºåºåå¤è¾¹å½¢å
é¨ |
| | | closeSceneList.forEach { |
| | | val point = it!!.longitude.toDouble() to it.latitude.toDouble() |
| | | if (MapUtil.isPointInPolygon(point, closePolygonTmp)) { |
| | | result.add(it) |
| | | } |
| | | } |
| | | |
| | | // æ ¹æ®æ±¡æå åçé级ï¼è®¡ç®ä¸»è¦ç污æåºæ¯ç±»åï¼çéç»æ |
| | | val mainSceneType = calSceneType(pollutedData) |
| | | if (mainSceneType != null) { |
| | | // this.conclusion = mainSceneType.first |
| | | result = result.filter { |
| | | val r = mainSceneType.second.find { s -> |
| | | s.value == it.typeId.toInt() |
| | | } |
| | | r != null |
| | | }.toMutableList() |
| | | } |
| | | this.sceneList = findClosestStation(sceneInfoRep, result) |
| | | } |
| | | |
| | | val txt = summaryTxt(pollutedData, this.sceneList!!) |
| | | this.conclusion = txt |
| | |
| | | // 氮氧ååç©ï¼ä¸è¬ç±äºæºå¨è½¦å°¾æ°ï¼åæ¥è®¡ç®CO |
| | | FactorType.NO2 -> { |
| | | val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000 |
| | | "氮氧ååç©åé«ï¼COçé级为${coAvg}mg/m³ï¼ä¸è¬ç±äºæºå¨è½¦å°¾æ°é æï¼æ±¡ææºä»¥æ±½ä¿®ãå æ²¹ç«ä¸ºä¸»" to |
| | | "氮氧ååç©åé«ï¼COçé级为${coAvg}mg/m³ï¼ä¸è¬ç±äºæºå¨è½¦å°¾æ°é æï¼æ±¡ææºä»¥æ±½ä¿®ãå æ²¹ç«ä¸ºä¸»" to |
| | | listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17) |
| | | } |
| | | |
| | | FactorType.CO -> null |
| | | FactorType.CO -> null |
| | | |
| | | FactorType.H2S -> null |
| | | FactorType.H2S -> null |
| | | |
| | | FactorType.SO2 -> null |
| | | FactorType.SO2 -> null |
| | | |
| | | FactorType.O3 -> null |
| | | FactorType.O3 -> null |
| | | // a) pm2.5ãpm10ç¹å«é«ï¼ä¸¤è
å¨åæ
åµä¸åæ¥å±ç¤ºï¼pm2.5å pm10çæ¯éååï¼æ¯éè¶é«ï¼è¶æå¯è½æ¯é¤é¥® |
| | | // b) pm10ç¹å«é«ãpm2.5è¾é«ï¼å¤§é¢ç²æ¬å°æ±¡æï¼åªå±ç¤ºpm10ï¼pm2.5å pm10çæ¯éååï¼å·¥å°ä¸ºä¸» |
| | | FactorType.PM25, |
| | |
| | | }.average() |
| | | val str = |
| | | "PM2.5é级为${pm25Avg}μg/m³ï¼PM10é级为${pm10Avg}μg/m³ï¼PM2.5å PM10çæ¯é为${round(percentageAvg * 100)}%" |
| | | if (percentageAvg > 0.666) { |
| | | if (percentageAvg > 0.666) { |
| | | "${str}ï¼æ¯éè¾å¤§ï¼æ±¡ææºä»¥é¤é¥®ä¸ºä¸»ï¼å·¥å°æ¬¡ä¹" to |
| | | listOf( |
| | | SceneType.TYPE1, |
| | |
| | | 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åé«ï¼åæ¶PM2.5é级为${pm25Avg}μg/m³ï¼COé级为${coAvg}mg/m³ï¼O3é级为${o3Avg}μg/mÂ³ï¼æ±¡ææºä»¥æ±½ä¿®ãå æ²¹ç«ä¸ºä¸»" to |
| | | "VOCåé«ï¼åæ¶PM2.5é级为${pm25Avg}μg/m³ï¼COé级为${coAvg}mg/m³ï¼O3é级为${o3Avg}μg/mÂ³ï¼æ±¡ææºä»¥æ±½ä¿®ãå æ²¹ç«ä¸ºä¸»" to |
| | | listOf(SceneType.TYPE6, SceneType.TYPE17, SceneType.TYPE12) |
| | | } |
| | | |
| | | else -> null |
| | | else -> null |
| | | } |
| | | des = res?.first |
| | | res?.second?.let { sceneTypes.addAll(it) } |
| | |
| | | val et = DateUtil.instance.getTime(pollutedData.endTime) |
| | | var txt = |
| | | "å¨${st}è³${et}ä¹é´ï¼åºç°${pollutedData.exception}" |
| | | pollutedData.statisticMap.entries.forEach {s -> |
| | | 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()) { |
| | |
| | | |
| | | // å½åçèµ°èªæ°æ®çå®ä½åæ±¡ææºè·ç¦»æ¯å¦æ¯éæ¸æ¥è¿ï¼è¥èµ°èªè¿ç¦»äºä¸»è¦æ±¡ææºï¼æç¤ºç¨æ·è°æ´èµ°èªè·¯çº¿ |
| | | if (!result.sortedSceneList.isNullOrEmpty()) { |
| | | val sT = DateUtil.instance.dateToString(clueList.first().pollutedData?.startTime, DateUtil.DateStyle.HH_MM_SS) |
| | | val sT = |
| | | DateUtil.instance.dateToString(clueList.first().pollutedData?.startTime, DateUtil.DateStyle.HH_MM_SS) |
| | | val eT = DateUtil.instance.dateToString(clueList.last().pollutedData?.endTime, DateUtil.DateStyle.HH_MM_SS) |
| | | val closetScene = result.sortedSceneList?.first() |
| | | // èµ°èªè·¯çº¿è°æ´å»ºè®® |
| | |
| | | val origin = MapUtil.wgs84ToGcj02(lastP.longitude!!.toDouble() to lastP.latitude!!.toDouble()) |
| | | val destination = closetScene.first!!.longitude.toDouble() to closetScene.first!!.latitude.toDouble() |
| | | |
| | | // 建议çèµ°èªè·¯çº¿ |
| | | result.direction = AMapService.directionDriving(origin, destination) |
| | | // Thread.sleep(200) |
| | | if (config.isSearchAddress) { |
| | | // 建议çèµ°èªè·¯çº¿ |
| | | result.direction = AMapService.directionDriving(origin, destination) |
| | | } |
| | | } |
| | | // 线索åæå®æåï¼ç§»å¨è³åå²çº¿ç´¢å表 |
| | | historyClueList.addAll(clueList) |
| | |
| | | @Table(name = "source_trace_msg") |
| | | public class SourceTraceMsg { |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Integer id; |
| | | |
| | | @Column(name = "device_code") |
| | |
| | | |
| | | @Column(name = "create_time") |
| | | private Date createTime; |
| | | |
| | | private String content; |
| | | |
| | | /** |
| | | * @return id |
| | |
| | | */ |
| | | public void setCreateTime(Date createTime) { |
| | | this.createTime = createTime; |
| | | } |
| | | |
| | | /** |
| | | * @return content |
| | | */ |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | |
| | | /** |
| | | * @param content |
| | | */ |
| | | public void setContent(String content) { |
| | | this.content = content == null ? null : content.trim(); |
| | | } |
| | | } |
| | |
| | | import com.flightfeather.uav.domain.MyMapper |
| | | import com.flightfeather.uav.domain.entity.SourceTraceMsg |
| | | import org.apache.ibatis.annotations.Mapper |
| | | import org.apache.ibatis.annotations.Select |
| | | |
| | | @Mapper |
| | | interface SourceTraceMsgMapper : MyMapper<SourceTraceMsg?> |
| | | interface SourceTraceMsgMapper : MyMapper<SourceTraceMsg?> { |
| | | |
| | | /** |
| | | * éç½®èªå¢id |
| | | */ |
| | | @Select("alter table source_trace_msg auto_increment = #{param1}") |
| | | fun resetAutoIncrement(id: Int) |
| | | } |
| | |
| | | import com.flightfeather.uav.common.utils.GsonUtils |
| | | import com.flightfeather.uav.domain.entity.Mission |
| | | import com.flightfeather.uav.domain.entity.SourceTraceMsg |
| | | import com.flightfeather.uav.domain.entity.SourceTraceMsgBlob |
| | | import com.flightfeather.uav.domain.mapper.SourceTraceMsgBlobMapper |
| | | import com.flightfeather.uav.domain.mapper.SourceTraceMsgMapper |
| | | import com.flightfeather.uav.socket.sender.MsgType |
| | | import org.springframework.stereotype.Repository |
| | | import org.springframework.transaction.annotation.Transactional |
| | | import tk.mybatis.mapper.entity.Example |
| | | import java.util.* |
| | | |
| | |
| | | * @author feiyu02 |
| | | */ |
| | | @Repository |
| | | class SourceTraceRep(private val sourceTraceMsgMapper: SourceTraceMsgMapper) { |
| | | class SourceTraceRep( |
| | | private val sourceTraceMsgMapper: SourceTraceMsgMapper, |
| | | private val sourceTraceMsgBlobMapper: SourceTraceMsgBlobMapper, |
| | | ) { |
| | | |
| | | private fun insertBlob(stm: SourceTraceMsg, json: String) { |
| | | sourceTraceMsgBlobMapper.insert(SourceTraceMsgBlob().apply { |
| | | msgId = stm.id |
| | | content = json |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * æå
¥æº¯æºä¿¡æ¯åæéä¿¡æ¯ |
| | | */ |
| | | @Transactional |
| | | fun insert(msgType: MsgType, obj: PollutedClue): Int { |
| | | val stm = SourceTraceMsg().apply { |
| | | deviceCode = obj.deviceCode |
| | |
| | | startTime = obj.pollutedData?.startTime |
| | | endTime = obj.pollutedData?.endTime |
| | | this.msgType = msgType.value |
| | | content = GsonUtils.gson.toJson(obj) |
| | | createTime = Date() |
| | | } |
| | | return if (fetchOneExist(stm) == null) { |
| | | sourceTraceMsgMapper.insert(stm) |
| | | val c = sourceTraceMsgMapper.insert(stm) |
| | | insertBlob(stm, GsonUtils.gson.toJson(obj)) |
| | | c |
| | | } else { |
| | | 0 |
| | | } |
| | |
| | | /** |
| | | * æå
¥çº¿ç´¢ä¿¡æ¯ |
| | | */ |
| | | @Transactional |
| | | fun insert(res: AnalysisResult): Int { |
| | | val stm = SourceTraceMsg().apply { |
| | | deviceCode = res.deviceCode |
| | | startTime = res.time |
| | | endTime = res.time |
| | | this.msgType = MsgType.AnaResult.value |
| | | content = GsonUtils.gson.toJson(res) |
| | | createTime = Date() |
| | | } |
| | | return if (fetchOneExist(stm) == null) { |
| | | sourceTraceMsgMapper.insert(stm) |
| | | val c = sourceTraceMsgMapper.insert(stm) |
| | | insertBlob(stm, GsonUtils.gson.toJson(res)) |
| | | c |
| | | } else { |
| | | 0 |
| | | } |
| | |
| | | } |
| | | |
| | | fun fetchList(deviceCode: String, startTime: Date, endTime: Date): List<BaseExceptionResult?> { |
| | | return sourceTraceMsgMapper.selectByExample(Example(SourceTraceMsg::class.java).apply { |
| | | createCriteria().andEqualTo("deviceCode", deviceCode) |
| | | .andGreaterThanOrEqualTo("startTime", startTime) |
| | | .andLessThanOrEqualTo("endTime", endTime) |
| | | orderBy("id").desc() |
| | | }).map { stm -> |
| | | return sourceTraceMsgBlobMapper.selectWithBlob(deviceCode, startTime, endTime) |
| | | .map { stm -> |
| | | when (stm?.msgType) { |
| | | MsgType.PolClue.value, |
| | | MsgType.DataChange.value, |
| | | -> { |
| | | GsonUtils.gson.fromJson(stm.content, PollutedClue::class.java) |
| | | GsonUtils.gson.fromJson(stm.blobContent, PollutedClue::class.java) |
| | | } |
| | | |
| | | MsgType.AnaResult.value -> { |
| | | GsonUtils.gson.fromJson(stm.content, AnalysisResult::class.java) |
| | | GsonUtils.gson.fromJson(stm.blobContent, AnalysisResult::class.java) |
| | | } |
| | | |
| | | else -> null |
| | |
| | | } |
| | | } |
| | | |
| | | @Transactional |
| | | fun delete(mission: Mission): Int { |
| | | return sourceTraceMsgMapper.deleteByExample(Example(SourceTraceMsg::class.java).apply { |
| | | val idList = sourceTraceMsgMapper.selectByExample(Example(SourceTraceMsg::class.java).apply { |
| | | createCriteria().andEqualTo("deviceCode", mission.deviceCode) |
| | | .andGreaterThanOrEqualTo("startTime", mission.startTime) |
| | | .andLessThanOrEqualTo("endTime", mission.endTime) |
| | | }).map { it?.id } |
| | | sourceTraceMsgMapper.deleteByExample(Example(SourceTraceMsg::class.java).apply { |
| | | createCriteria().andIn("id", idList) |
| | | }) |
| | | return sourceTraceMsgBlobMapper.deleteByExample(Example(SourceTraceMsgBlob::class.java).apply { |
| | | createCriteria().andIn("msgId", idList) |
| | | }) |
| | | } |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.flightfeather.uav.lightshare.bean |
| | | |
| | | import com.flightfeather.uav.domain.entity.SourceTraceMsg |
| | | |
| | | /** |
| | | * |
| | | * @date 2025/8/6 |
| | | * @author feiyu02 |
| | | */ |
| | | class SourceTraceMsgVo : SourceTraceMsg() { |
| | | |
| | | var blobId: Int? = null |
| | | var blobMsgId: Int? = null |
| | | var blobContent: String? = null |
| | | } |
| | |
| | | <property name="suppressAllComments" value="true"/> |
| | | </commentGenerator> |
| | | <!--æ°æ®åºé¾æ¥URLï¼ç¨æ·åãå¯ç --> |
| | | <!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai"--> |
| | | <!-- userId="remoteU1"--> |
| | | <!-- password="eSoF8DnzfGTlhAjE">--> |
| | | <!-- </jdbcConnection>--> |
| | | <jdbcConnection driverClass="com.mysql.jdbc.Driver" |
| | | connectionURL="jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai" |
| | | userId="root" |
| | | password="123456"> |
| | | <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai" |
| | | userId="remoteU1" |
| | | password="eSoF8DnzfGTlhAjE"> |
| | | </jdbcConnection> |
| | | <!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver"--> |
| | | <!-- connectionURL="jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai"--> |
| | | <!-- userId="root"--> |
| | | <!-- password="123456">--> |
| | | <!-- </jdbcConnection>--> |
| | | <javaTypeResolver> |
| | | <property name="forceBigDecimals" value="false"/> |
| | | </javaTypeResolver> |
| | |
| | | <!-- <table tableName="grid_data" domainObjectName="GridData" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> |
| | | <!-- <table tableName="grid_data_detail" domainObjectName="GridDataDetail" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> |
| | | <table tableName="source_trace_msg" domainObjectName="SourceTraceMsg" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/> |
| | | <table tableName="source_trace_msg_blob" domainObjectName="SourceTraceMsgBlob" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/> |
| | | </context> |
| | | </generatorConfiguration> |
| | |
| | | <result column="msg_type" jdbcType="INTEGER" property="msgType" /> |
| | | <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> |
| | | </resultMap> |
| | | <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.flightfeather.uav.domain.entity.SourceTraceMsg"> |
| | | <!-- |
| | | WARNING - @mbg.generated |
| | | --> |
| | | <result column="content" jdbcType="LONGVARCHAR" property="content" /> |
| | | </resultMap> |
| | | <sql id="Base_Column_List"> |
| | | <!-- |
| | | WARNING - @mbg.generated |
| | | --> |
| | | id, device_code, factor_name, exception_type, start_time, end_time, msg_type, create_time |
| | | </sql> |
| | | <sql id="Blob_Column_List"> |
| | | <!-- |
| | | WARNING - @mbg.generated |
| | | --> |
| | | content |
| | | </sql> |
| | | </mapper> |
| | |
| | | BaseRealTimeData().apply { id = 3 }, |
| | | endData!! |
| | | ) |
| | | historyData = mutableListOf() |
| | | // historyData = mutableListOf() |
| | | exceptionExisted = true |
| | | exceptionCreated = false |
| | | // exceptionResult = |
| | |
| | | |
| | | @Test |
| | | fun checkDelayedExceptions() { |
| | | taskList().forEach { exc -> |
| | | val e = exceptions() |
| | | exc.remainingExceptions.add(RemainException(listOf(e[3], e[5]), listOf(FactorType.VOC, FactorType.PM25, FactorType.CO))) |
| | | exc.remainingExceptions.add(RemainException(listOf(e[1]), listOf(FactorType.VOC, FactorType.CO))) |
| | | exc.remainingExceptions.add(RemainException(listOf(e[3]), listOf(FactorType.PM10, FactorType.PM25))) |
| | | val res = listOf(FactorFilter.SelectedFactor(FactorType.PM25) to exceptionTag) |
| | | val c = listOf(FactorType.PM25, FactorType.PM10, FactorType.VOC) |
| | | val r = RemainException(res, c) |
| | | println(r) |
| | | // taskList().forEach { exc -> |
| | | // val e = exceptions() |
| | | // exc.remainingExceptions.add(RemainException(listOf(e[3], e[5]), listOf(FactorType.VOC, FactorType.PM25, FactorType.CO))) |
| | | // exc.remainingExceptions.add(RemainException(listOf(e[1]), listOf(FactorType.VOC, FactorType.CO))) |
| | | // exc.remainingExceptions.add(RemainException(listOf(e[3]), listOf(FactorType.PM10, FactorType.PM25))) |
| | | // |
| | | // exc.latestExceptions.clear() |
| | | // exc.latestExceptions.addAll(exceptions()) |
| | | // |
| | | // exc.removeSingleFactor(BaseRealTimeData()) |
| | | // |
| | | // val resList = exc.result.map { |
| | | // it.pollutedData?.statisticMap?.entries?.map { e -> e.key } |
| | | // } |
| | | // assertContentEquals( |
| | | // listOf(listOf(FactorType.NO2), listOf(FactorType.O3)), |
| | | // resList, |
| | | // "å¼å¸¸ç»æåºè¯¥é½æ¯ä¸å¨ç»åä¸çå¼å¸¸" |
| | | // ) |
| | | // |
| | | // val resList2 = exc.latestExceptions.map { it.first.main } |
| | | // assertContentEquals( |
| | | // listOf(FactorType.CO, FactorType.PM25, FactorType.PM10, FactorType.VOC), |
| | | // resList2, |
| | | // "å©ä½çåºè¯¥æ¯ä¸å¨ç»åä¸çå¼å¸¸" |
| | | // ) |
| | | // } |
| | | |
| | | exc.latestExceptions.clear() |
| | | exc.latestExceptions.addAll(exceptions()) |
| | | |
| | | exc.removeSingleFactor(BaseRealTimeData()) |
| | | |
| | | val resList = exc.result.map { |
| | | it.pollutedData?.statisticMap?.entries?.map { e -> e.key } |
| | | } |
| | | assertContentEquals( |
| | | listOf(listOf(FactorType.NO2), listOf(FactorType.O3)), |
| | | resList, |
| | | "å¼å¸¸ç»æåºè¯¥é½æ¯ä¸å¨ç»åä¸çå¼å¸¸" |
| | | ) |
| | | |
| | | val resList2 = exc.latestExceptions.map { it.first.main } |
| | | assertContentEquals( |
| | | listOf(FactorType.CO, FactorType.PM25, FactorType.PM10, FactorType.VOC), |
| | | resList2, |
| | | "å©ä½çåºè¯¥æ¯ä¸å¨ç»åä¸çå¼å¸¸" |
| | | ) |
| | | } |
| | | } |
| | | |
| | | @Test |
| | |
| | | FactorFilter.SelectedFactor(FactorType.CO), |
| | | ) |
| | | val factorList2 = listOf( |
| | | FactorFilter.SelectedFactor(FactorType.CO), |
| | | FactorFilter.SelectedFactor(FactorType.PM25), |
| | | FactorFilter.SelectedFactor(FactorType.PM10), |
| | | ) |
| | | println(factorList == factorList2) |
| | | |
| | | val factorList3 = listOf( |
| | | FactorFilter.SelectedFactor(FactorType.PM25), |
| | | FactorFilter.SelectedFactor(FactorType.VOC), |
| | | ) |
| | | |
| | | val comb = listOf( |
| | | listOf( |
| | | FactorFilter.SelectedFactor(FactorType.PM10), |
| | | FactorFilter.SelectedFactor(FactorType.PM25), |
| | | FactorFilter.SelectedFactor(FactorType.CO), |
| | | ), |
| | | listOf( |
| | | FactorFilter.SelectedFactor(FactorType.PM25), |
| | | FactorFilter.SelectedFactor(FactorType.PM10), |
| | | ), |
| | | listOf( |
| | | FactorFilter.SelectedFactor(FactorType.VOC), |
| | | FactorFilter.SelectedFactor(FactorType.PM25), |
| | | ) |
| | | ) |
| | | |
| | | val i = comb.indexOf(factorList) |
| | | println(i) |
| | | } |
| | | |
| | | @Test |
| | |
| | | @Test |
| | | fun autoSourceTrace() { |
| | | val sourceTraceController = SourceTraceController(sceneInfoRep, sourceTraceRep, false) |
| | | val mCode = listOf("SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)", |
| | | "SH-CN-20240830(04)", "SH-CN-20240823", "SH-CN-20240723(02)", "SH-CN-20250723(01)") |
| | | val mCode = listOf( |
| | | "SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)", |
| | | "SH-CN-20240830(04)", "SH-CN-20240823", |
| | | "SH-CN-20240723(02)", |
| | | // "SH-CN-20250723(01)" |
| | | ) |
| | | mCode.forEach { c-> |
| | | missionRep.findOne(c)?.let {m -> |
| | | val rtData = realTimeDataService.getSecondData( |
| | |
| | | |
| | | @Test |
| | | fun deleteSourceTrace() { |
| | | val mCode = listOf("SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)", |
| | | "SH-CN-20240830(04)", "SH-CN-20240823", "SH-CN-20240723(02)", "SH-CN-20250723(01)") |
| | | val mCode = listOf( |
| | | "SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)", |
| | | "SH-CN-20240830(04)", "SH-CN-20240823", |
| | | "SH-CN-20240723(02)", |
| | | // "SH-CN-20250723(01)" |
| | | ) |
| | | mCode.forEach {c -> |
| | | missionRep.findOne(c)?.let { |
| | | sourceTraceRep.delete(it) |
| | |
| | | fun testPollutionTrace() { |
| | | dataAnalysisService.pollutionTrace("SH-CN-20250116") |
| | | } |
| | | |
| | | @Test |
| | | fun fetchHistory() { |
| | | dataAnalysisService.fetchHistory("SH-CN-20250723(01)") |
| | | } |
| | | } |