| | |
| | | return this |
| | | } |
| | | |
| | | fun withCombination(com: List<List<FactorType>>):Builder{ |
| | | combination.addAll(com) |
| | | return this |
| | | } |
| | | |
| | | fun create(): FactorFilter { |
| | | return this@FactorFilter |
| | | } |
| | |
| | | // 所选因子集合 |
| | | val selectedList = mutableListOf<SelectedFactor>() |
| | | |
| | | // 因子的关联关系 |
| | | val combination = mutableListOf<List<FactorType>>() |
| | | |
| | | fun mainList(): List<FactorType> { |
| | | return selectedList.map { it.main } |
| | | } |
| | |
| | | * @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>>? |
| | | } |
| | | val factorFilter: FactorFilter, |
| | | ) |
| | |
| | | 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 { |
| | |
| | | // 末尾数据对象 |
| | | 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>() |
| | | |
| | | /** |
| | | * 立即判断:当出现异常时,缓存异常数据的同时,立即对已有异常进行判断是否满足异常结果要求 |
| | |
| | | lastData = data |
| | | |
| | | mergeExceptionResult() |
| | | clearExceptions(data) |
| | | } |
| | | |
| | | override fun onDone() { |
| | |
| | | */ |
| | | fun recordException(factor: FactorFilter.SelectedFactor, tag: T, data: BaseRealTimeData) { |
| | | checkResult(factor, ExceptionStatusType.Ended) |
| | | tag.refreshWithNextException(data) |
| | | // tag.refreshWithNextException(data) |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | 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) { |
| | |
| | | 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) { |
| | | // 将最新的异常的guid赋值给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) { |
| | | // // 将最新的异常的guid赋值给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) |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | 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) |
| | |
| | | // 如果组合内的所有因子都存在异常,则存储为合并异常 |
| | | 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 |
| | | |
| | | } |
| | |
| | | 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() |
| | | } |
| | | } |
| | |
| | | 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() |
| | | } |
| | | } |
| | |
| | | |
| | | fun addHistoryData(data: BaseRealTimeData) { |
| | | historyData.add(data) |
| | | if (historyData.size > 20) { |
| | | if (historyData.size > 15) { |
| | | historyData.removeAt(0) |
| | | } |
| | | } |
| | |
| | | this.sceneInfoRep = sceneInfoRep |
| | | this.sourceTraceRep = sourceTraceRep |
| | | this.config = if (factorFilter != null) { |
| | | RTExcWindLevelConfig(factorFilter, emptyList()) |
| | | RTExcWindLevelConfig(factorFilter) |
| | | } else { |
| | | RTExcWindLevelConfig( |
| | | FactorFilter.builder() |
| | |
| | | .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) } |
| | |
| | | } |
| | | |
| | | // 数据突变异常回调 |
| | | 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) { |
| | |
| | | * @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 |
| | |
| | | } |
| | | |
| | | 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( |
| | |
| | | |
| | | 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>) |
| | | // } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | // 异常数据生成回调类 |
| | | typealias NewPolluteClueCallback = (ex: List<PollutedClue>) -> Unit |
| | | typealias NewPolluteClueCallback = (ex: PollutedClue) -> Unit |
| | | /** |
| | | * 不同风速下,数据突变异常基类 |
| | | * @date 2025/5/29 |
| | |
| | | } |
| | | |
| | | 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, |
| | |
| | | |
| | | 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>) |
| | | // } |
| | | } |
| | | } |
| | | } |
| | |
| | | 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 -> |
| | |
| | | // 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++ |
| | | } |
| | |
| | | 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++ |
| | | } |
| | |
| | | * @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. 展示数据变化情况,上升速率等等 |
| | | */ |
| | |
| | | */ |
| | | 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、pm10特别高,两者在各情况下同步展示,pm2.5占pm10的比重变化,比重越高,越有可能是餐饮 |
| | |
| | | constructor( |
| | | start: BaseRealTimeData, |
| | | end: BaseRealTimeData?, |
| | | factor: FactorFilter.SelectedFactor, |
| | | factorList: List<FactorFilter.SelectedFactor>, |
| | | exceptionData: List<BaseRealTimeData>, |
| | | historyData: List<BaseRealTimeData>, |
| | | eType: ExceptionType, |
| | |
| | | ) : 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 |
| | |
| | | endData = end |
| | | |
| | | windSpeed = exceptionData.first().windSpeed?.toDouble() |
| | | percentage = windLevelCondition?.mutationRate?.first |
| | | times = windLevelCondition?.countLimit |
| | | |
| | | dataList.add(start) |
| | |
| | | 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 |
| | |
| | | // 风速 |
| | | 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 |
| | | |
| | |
| | | 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, |
| | |
| | | */ |
| | | @Throws(Exception::class) |
| | | private fun calSceneType(pollutedData: PollutedData): Pair<String, List<SceneType>>? { |
| | | when (pollutedData.selectedFactor?.main) { |
| | | // 氮氧化合物,一般由于机动车尾气,同步计算CO |
| | | 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、pm10特别高,两者在各情况下同步展示,pm2.5占pm10的比重变化,比重越高,越有可能是餐饮 |
| | | // b) pm10特别高、pm2.5较高,大颗粒扬尘污染,只展示pm10,pm2.5占pm10的比重变化,工地为主 |
| | | 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 |
| | | // 计算异常数据的pm2.5占pm10比重的均值 |
| | | val percentageAvg = pollutedData.dataList.map { |
| | | it.pm25!! / it.pm10!! |
| | | }.average() |
| | | val str = |
| | | "PM2.5量级为${pm25Avg}μg/m³,PM10量级为${pm10Avg}μg/m³,PM2.5占PM10的比重为${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) { |
| | | // 氮氧化合物,一般由于机动车尾气,同步计算CO |
| | | 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较高,同比计算pm2.5的量级,可能存在同步偏高(汽修、加油站), 同步计算O3是否有高值 |
| | | // d) VOC较高,处于加油站(车辆拥堵情况),CO一般较高, 同步计算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偏高,同时PM2.5量级为${pm25Avg}μg/m³,CO量级为${coAvg}mg/m³,O3量级为${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、pm10特别高,两者在各情况下同步展示,pm2.5占pm10的比重变化,比重越高,越有可能是餐饮 |
| | | // b) pm10特别高、pm2.5较高,大颗粒扬尘污染,只展示pm10,pm2.5占pm10的比重变化,工地为主 |
| | | 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 |
| | | // 计算异常数据的pm2.5占pm10比重的均值 |
| | | val percentageAvg = pollutedData.dataList.map { |
| | | it.pm25!! / it.pm10!! |
| | | }.average() |
| | | val str = |
| | | "PM2.5量级为${pm25Avg}μg/m³,PM10量级为${pm10Avg}μg/m³,PM2.5占PM10的比重为${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较高,同比计算pm2.5的量级,可能存在同步偏高(汽修、加油站), 同步计算O3是否有高值 |
| | | // d) VOC较高,处于加油站(车辆拥堵情况),CO一般较高, 同步计算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偏高,同时PM2.5量级为${pm25Avg}μg/m³,CO量级为${coAvg}mg/m³,O3量级为${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 |
| | | } |
| | | |
| | | /** |
| | |
| | | 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 { |
| | |
| | | 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 |