feiyu02
23 小时以前 8eb584869b4fd4de0f51c93f2616f12e51df9193
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
@@ -1,10 +1,12 @@
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
@@ -16,13 +18,78 @@
 * @author feiyu02
 */
class PollutedData() {
    companion object {
        // 默认数据采样时间间隔,单位:秒
        const val DEFAULT_PERIOD = 4
    }
    /**
     * 异常数据分组情况统计
     */
    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
        /**
         * 获取异常数据的第一个数据
         * !!!!第一个数据其实是首个异常数据的前一个数据值!!!!
         */
        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)
        }
        /**
         * 获取异常数据的最后一个数据
         */
        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
@@ -32,119 +99,176 @@
        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
        }
    }
    /**
     * 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
     */
    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
                }
            }
    constructor(
        start: BaseRealTimeData,
        end: BaseRealTimeData?,
        factorList: List<FactorFilter.SelectedFactor>,
        exceptionData: List<BaseRealTimeData>,
        historyData: List<BaseRealTimeData>,
        eType: ExceptionType,
        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
    ) : this() {
            // 将采样时间最晚的作为结束数据
            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 = start.dataTime
        endTime = end?.dataTime
//        startData = start.getByFactorType(factor.main)
//        endData = end?.getByFactorType(factor.main) ?: startData
        startData = start
        endData = end
        windSpeed = exceptionData.first().windSpeed?.toDouble()
        times = windLevelCondition?.countLimit
        dataList.add(start)
        exceptionData.forEach {
            dataList.add(it)
        }
        dataVoList.addAll(dataList.map { it.toDataVo() })
        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)
                    }
                }
        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)
                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 startTime: Date? = null
    var endTime: Date? = null
    var startData: BaseRealTimeData? = null
    var endData: BaseRealTimeData? = null
    // 风速
    var windSpeed: Double? = null
    // 发生次数
    var times: Int? = null
    var historyDataList = mutableListOf<DataVo>()
    // 异常监测数据
    var dataList: MutableList<BaseRealTimeData> = mutableListOf()
    var dataVoList: MutableList<DataVo> = mutableListOf()
    // 异常监测数据,包含单次异常中所有发生了异常的数据值(可能不是时间连续的数据)
//    var dataList: MutableList<BaseRealTimeData> = mutableListOf()
//    var dataVoList: MutableList<DataVo> = mutableListOf()
    var statisticMap = mutableMapOf<FactorType, Statistic>()
    /**
     * 获取所有异常因子名称
     */
    fun toFactorNames(): String {
        val factors = statisticMap.entries.map { it.key }.sortedBy { it.value }.joinToString(";") { it.des }
        return factors
    }
    private fun calPer(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) ?: .0f
            val n = list[i + 1].getByFactorType(factorType) ?: .0f
            total += (n - p) / p
        }
        return total / (list.size - 1)
    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 calRate(factorType: FactorType): Double? {
        val list = dataList
        if (list.size < 2) return null
    private fun calPer(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Double? {
        if (exceptionData.size < 2) return null
        var total = .0
        for (i in 0 until list.size - 1) {
            val p = list[i].getByFactorType(factorType) ?: .0f
            val n = list[i + 1].getByFactorType(factorType) ?: .0f
        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
        }
        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 / (list.size - 1)
        return total / (exceptionData.size - 1)
    }
    private fun dataSummary(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Triple<Double, Double,