| | |
| | | package com.flightfeather.uav.biz.sourcetrace.model |
| | | |
| | | import com.flightfeather.uav.biz.FactorFilter |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag |
| | | import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType |
| | | import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig |
| | | import com.flightfeather.uav.common.utils.DateUtil |
| | | import com.flightfeather.uav.domain.entity.BaseRealTimeData |
| | | import com.flightfeather.uav.domain.entity.avg |
| | | import com.flightfeather.uav.lightshare.bean.DataVo |
| | | import com.flightfeather.uav.socket.eunm.FactorType |
| | | import java.util.Date |
| | | import kotlin.math.round |
| | | |
| | | /** |
| | | * 污染数据 |
| | |
| | | * @author feiyu02 |
| | | */ |
| | | class PollutedData() { |
| | | |
| | | companion object { |
| | | // 默认数据采样时间间隔,单位:秒 |
| | | const val DEFAULT_PERIOD = 4 |
| | | } |
| | | |
| | | /** |
| | | * 9. 关联因子 |
| | | * a) pm2.5、pm10特别高,两者在各情况下同步展示,pm2.5占pm10的比重变化,比重越高,越有可能是餐饮 |
| | | * b) pm10特别高、pm2.5较高,大颗粒扬尘污染,只展示pm10,pm2.5占pm10的比重变化,工地为主 |
| | | * c) VOC较高,同比计算pm2.5的量级,可能存在同步偏高(汽修、加油站), 同步计算O3是否有高值 |
| | | * d) VOC较高,处于加油站(车辆拥堵情况),CO一般较高, 同步计算O3是否有高值 |
| | | * e) 氮氧化合物,一般由于机动车尾气,同步计算CO |
| | | * 异常数据分组情况统计 |
| | | */ |
| | | inner class ExcGroup{ |
| | | constructor(dataIndexList: List<Int>, factorType: FactorType){ |
| | | this.dataIndexList = dataIndexList |
| | | this.factorType = factorType |
| | | val first = getFirstDataValue()?.toDouble() |
| | | val last = getLastDataValue()?.toDouble() |
| | | if (first != null && last != null) { |
| | | per = round((last - first) / first * 100) / 100 |
| | | rate = round((last - first) / DEFAULT_PERIOD * 100) / 100 |
| | | } |
| | | } |
| | | var factorType: FactorType? = null |
| | | /** |
| | | * 异常数据对应历史数据[historyDataList]中的索引值 |
| | | */ |
| | | var dataIndexList: List<Int>? = null |
| | | // 变化幅度 |
| | | var per: Double? = null |
| | | // 变化速率 |
| | | var rate: Double? = null |
| | | |
| | | constructor( |
| | | start: BaseRealTimeData, |
| | | end: BaseRealTimeData?, |
| | | factor: FactorFilter.SelectedFactor, |
| | | exceptionData: List<BaseRealTimeData>, |
| | | eType: ExceptionType, |
| | | windLevelCondition: RTExcWindLevelConfig.WindLevelCondition, |
| | | ) : this() { |
| | | exception = eType.des |
| | | exceptionType = eType.value |
| | | factorId = factor.main.value |
| | | factorName = factor.main.des |
| | | subFactorId = factor.subs.map { it.value } |
| | | subFactorName = factor.subs.map { it.des } |
| | | selectedFactor = factor |
| | | |
| | | startTime = DateUtil.instance.dateToString(start.dataTime, DateUtil.DateStyle.HH_MM_SS) |
| | | endTime = DateUtil.instance.dateToString(end?.dataTime, DateUtil.DateStyle.HH_MM_SS) ?: startTime |
| | | startData = start.getByFactorType(factor.main) |
| | | endData = end?.getByFactorType(factor.main) ?: startData |
| | | |
| | | windSpeed = exceptionData.first().windSpeed?.toDouble() |
| | | percentage = windLevelCondition.mutationRate.first |
| | | times = windLevelCondition.countLimit |
| | | |
| | | exceptionData.forEach { |
| | | dataList.add(it) |
| | | dataVoList.add(it.toDataVo()) |
| | | /** |
| | | * 获取异常数据的第一个数据 |
| | | * !!!!第一个数据其实是首个异常数据的前一个数据值!!!! |
| | | */ |
| | | fun getFirstData(): BaseRealTimeData? { |
| | | return dataIndexList?.firstOrNull()?.let { |
| | | val i = if (it > 0) it - 1 else it |
| | | historyDataList[i].toBaseRealTimeData(BaseRealTimeData::class.java) |
| | | } |
| | | } |
| | | fun getFirstDataValue(): Float? { |
| | | return getFirstData()?.getByFactorType(factorType) |
| | | } |
| | | |
| | | calPer() |
| | | /** |
| | | * 获取异常数据的最后一个数据 |
| | | */ |
| | | fun getLastData(): BaseRealTimeData? { |
| | | return dataIndexList?.lastOrNull()?.let { |
| | | historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java) |
| | | } |
| | | } |
| | | fun getLastDataValue(): Float? { |
| | | return getLastData()?.getByFactorType(factorType) |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 各监测因子异常统计信息 |
| | | */ |
| | | 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 |
| | | |
| | | /** |
| | | * 异常数据对应历史数据[historyDataList]中的索引值 |
| | | */ |
| | | var dataIndexList: List<Int>? = null |
| | | |
| | | // 因子量级平均变化幅度 |
| | | var avgPer: Double? = null |
| | | // 因子量级平均变化速率 |
| | | var avgRate: Double? = null |
| | | |
| | | var avg: Double? = null |
| | | var min: Double? = null |
| | | var max: Double? = null |
| | | |
| | | var excGroup: List<ExcGroup>? = null |
| | | |
| | | /** |
| | | * 获取异常数据 |
| | | */ |
| | | fun getExceptionData(): List<BaseRealTimeData>? { |
| | | return dataIndexList?.map { historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java) } |
| | | } |
| | | |
| | | /** |
| | | * 获取异常数据分段情况 |
| | | * 将连续的异常数据分为一组 |
| | | */ |
| | | fun getExceptionDataGroup(): List<List<Int>> { |
| | | val res = mutableListOf<MutableList<Int>>() |
| | | var curGroup = mutableListOf<Int>() |
| | | var lastIndex = -2 |
| | | dataIndexList?.forEach { |
| | | if (curGroup.isEmpty()) { |
| | | curGroup.add(it) |
| | | } else if (it - lastIndex == 1) { |
| | | curGroup.add(it) |
| | | } else { |
| | | res.add(curGroup) |
| | | curGroup = mutableListOf(it) |
| | | } |
| | | lastIndex = it |
| | | } |
| | | if (curGroup.isNotEmpty()) { |
| | | res.add(curGroup) |
| | | } |
| | | return res |
| | | } |
| | | } |
| | | |
| | | constructor(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>, eType: ExceptionType,) : this() { |
| | | // 遍历所有的因子的异常,整合统一的异常结果,具体如下 |
| | | var startData: BaseRealTimeData? = null |
| | | var endData: BaseRealTimeData? = null |
| | | var historyData = mutableListOf<BaseRealTimeData>() |
| | | var _times = 0 |
| | | 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 (historyData.isEmpty()) { |
| | | historyData = e.second.historyData |
| | | } else { |
| | | e.second.historyData.forEach { |
| | | if (historyData.find { d -> d.dataTime == it.dataTime } == null) { |
| | | historyData.add(it) |
| | | } |
| | | } |
| | | } |
| | | |
| | | _times += e.second.exceptionData.size |
| | | } |
| | | // 按照采样时间升序排列 |
| | | historyData.sortBy { it.dataTime } |
| | | |
| | | exception = eType.des |
| | | exceptionType = eType.value |
| | | startTime = startData?.dataTime |
| | | endTime = endData?.dataTime |
| | | windSpeed = historyData.avg().windSpeed?.toDouble() |
| | | times = _times |
| | | historyDataList.addAll(historyData.map { it.toDataVo() }) |
| | | |
| | | // 再次整合异常数据,分别计算各因子的异常结果统计 |
| | | exceptions.forEach {e -> |
| | | statisticMap[e.first.main] = Statistic().apply { |
| | | factorId = e.first.main.value |
| | | factorName = e.first.main.des |
| | | subFactorId = e.first.subs.map { it.value } |
| | | subFactorName = e.first.subs.map { it.des } |
| | | selectedFactor = e.first |
| | | dataIndexList = e.second.exceptionData.map { |
| | | historyDataList.indexOfFirst { d -> |
| | | d.time == DateUtil.instance.dateToString(it.dataTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS) |
| | | } |
| | | } |
| | | |
| | | val s = dataSummary(e.second.exceptionData, e.first.main) |
| | | avg = s.first |
| | | min = s.second |
| | | max = s.third |
| | | |
| | | excGroup = getExceptionDataGroup().map { ExcGroup(it, e.first.main) } |
| | | avgPer = excGroup?.mapNotNull { it.per }?.average() |
| | | avgRate = excGroup?.mapNotNull { it.rate }?.average() |
| | | } |
| | | } |
| | | } |
| | | |
| | | var deviceCode: String? = null |
| | | |
| | | var exception: String? = null |
| | | var exceptionType: Int? = null |
| | | |
| | | var factorId: Int? = null |
| | | var factorName: String? = null |
| | | var subFactorId: List<Int>? = null |
| | | var subFactorName: List<String>? = null |
| | | var selectedFactor: FactorFilter.SelectedFactor? = null |
| | | |
| | | var startTime: String? = null |
| | | var endTime: String? = null |
| | | |
| | | var startData: Float? = null |
| | | var endData: Float? = null |
| | | |
| | | var startTime: Date? = null |
| | | var endTime: Date? = null |
| | | // 风速 |
| | | var windSpeed: Double? = null |
| | | |
| | | // 因子量级变化幅度 |
| | | var percentage: Double? = null |
| | | // 因子量级平均变化幅度 |
| | | var avgPer: Double? = null |
| | | |
| | | // 发生次数 |
| | | var times: Int? = null |
| | | var historyDataList = mutableListOf<DataVo>() |
| | | // 异常监测数据,包含单次异常中所有发生了异常的数据值(可能不是时间连续的数据) |
| | | // var dataList: MutableList<BaseRealTimeData> = mutableListOf() |
| | | // var dataVoList: MutableList<DataVo> = mutableListOf() |
| | | var statisticMap = mutableMapOf<FactorType, Statistic>() |
| | | |
| | | // 异常监测数据 |
| | | var dataList: MutableList<BaseRealTimeData> = mutableListOf() |
| | | var dataVoList: MutableList<DataVo> = mutableListOf() |
| | | /** |
| | | * 获取所有异常因子名称 |
| | | */ |
| | | fun toFactorNames(): String { |
| | | val factors = statisticMap.entries.map { it.key }.sortedBy { it.value }.joinToString(";") { it.des } |
| | | return factors |
| | | } |
| | | |
| | | private fun calPer() { |
| | | if (dataList.size < 2) return |
| | | fun getExceptionAvgData(): BaseRealTimeData { |
| | | val exceptionDataList = statisticMap.flatMap { it.value.getExceptionData() ?: emptyList() } |
| | | val avgData = exceptionDataList.avg() |
| | | return avgData |
| | | } |
| | | /** |
| | | * 获取异常数据中心坐标(异常数据中经度纬度的平均值) |
| | | */ |
| | | fun getExceptionCenter(): Pair<Double, Double>? { |
| | | val avgData = getExceptionAvgData() |
| | | val wgs84Lng = avgData.longitude?.toDouble() |
| | | val wgs84Lat = avgData.latitude?.toDouble() |
| | | return if (wgs84Lng == null || wgs84Lat == null) null else Pair(wgs84Lng, wgs84Lat) |
| | | } |
| | | |
| | | private fun calPer(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Double? { |
| | | if (exceptionData.size < 2) return null |
| | | |
| | | var total = .0 |
| | | for (i in 0 until dataList.size - 1) { |
| | | total += dataList[i].getByFactorType(selectedFactor!!.main)!! |
| | | for (i in 0 until exceptionData.size - 1) { |
| | | val p = exceptionData[i]?.getByFactorType(factorType) ?: .0f |
| | | val n = exceptionData[i + 1]?.getByFactorType(factorType) ?: .0f |
| | | total += (n - p) / p |
| | | } |
| | | avgPer = total / (dataList.size - 1) |
| | | return total / (exceptionData.size - 1) |
| | | } |
| | | |
| | | private fun calRate(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Double? { |
| | | if (exceptionData.size < 2) return null |
| | | |
| | | var total = .0 |
| | | for (i in 0 until exceptionData.size - 1) { |
| | | val p = exceptionData[i]?.getByFactorType(factorType) ?: .0f |
| | | val n = exceptionData[i + 1]?.getByFactorType(factorType) ?: .0f |
| | | total += (n - p) / 4 |
| | | } |
| | | return total / (exceptionData.size - 1) |
| | | } |
| | | |
| | | private fun dataSummary(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Triple<Double, Double, |
| | | Double> { |
| | | var min = -1.0 |
| | | var max = -1.0 |
| | | var total = .0 |
| | | var count = 0 |
| | | exceptionData.forEach { |
| | | val value = it?.getByFactorType(factorType)?.toDouble() ?: return@forEach |
| | | if (min == -1.0 || min > value) { |
| | | min = round(value * 1000) / 1000 |
| | | } |
| | | if (max == -1.0 || max < value) { |
| | | max = round(value * 1000) / 1000 |
| | | } |
| | | total += value |
| | | count++ |
| | | } |
| | | val avg = if (count == 0) .0 else round(total / count * 1000) / 1000 |
| | | return Triple(avg, min, max) |
| | | } |
| | | } |