package com.flightfeather.uav.biz.dataanalysis.exceptiontype
|
|
import com.flightfeather.uav.biz.FactorFilter
|
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis
|
import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig
|
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
|
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
|
import com.flightfeather.uav.domain.entity.BaseRealTimeData
|
import com.flightfeather.uav.socket.eunm.FactorType
|
import kotlin.math.abs
|
|
/**
|
* 滑动平均值突变异常
|
*/
|
class ExceptionSlideAverage(config: DataAnalysisConfig) :
|
BaseExceptionAnalysis<DataAnalysisConfig, ExceptionResult>(config) {
|
|
private val historyDataList = mutableListOf<BaseRealTimeData>()
|
private val tempDataList = mutableListOf<BaseRealTimeData>()
|
private var lastData: BaseRealTimeData? = null
|
// private val avgListReverse = mutableListOf<Pair<Double, Boolean>>()
|
// private var startData: BaseRealTimeData? = null
|
// private var sIndex = 0
|
// private var eIndex = -1
|
// private var existException = false
|
|
inner class Tag {
|
// 起始数据下标
|
var sIndex = 0
|
|
// 起始数据对象
|
var startData: BaseRealTimeData? = null
|
|
// 末尾数据下标
|
var eIndex = -1
|
|
// 末尾数据对象
|
var endData: BaseRealTimeData? = null
|
|
// 数据组均值的集合
|
val avgListReverse = mutableListOf<Pair<Double, Boolean>>()
|
|
// 异常数据段
|
var exceptionData = mutableListOf<BaseRealTimeData>()
|
|
// 是否存在异常
|
var existException = false
|
|
fun refreshAfterCheckResult(data: BaseRealTimeData) {
|
// 判断并更新起始点位置
|
// val len = config.changeTrendGroup - 1 + config.changeTrendTimes + config.changeTrendInterval
|
val len = config.changeTrendGroup
|
if ((eIndex - sIndex + 1) > len) {
|
sIndex = eIndex + 1 - len
|
startData = historyDataList[sIndex]
|
exceptionData.clear()
|
exceptionData.addAll(historyDataList.subList(sIndex, eIndex + 1))
|
}
|
}
|
}
|
|
protected val tagMap = mutableMapOf<FactorType, Tag>()
|
|
override fun init() {
|
super.init()
|
historyDataList.clear()
|
tempDataList.clear()
|
lastData = null
|
|
tagMap.clear()
|
config.factorFilter.mainList().forEach { f ->
|
tagMap[f] = Tag()
|
}
|
// avgListReverse.clear()
|
// startData = null
|
// sIndex = 0
|
// eIndex = -1
|
// existException = false
|
}
|
|
override fun getExceptionType(): ExceptionType = 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.existException = true
|
it.exceptionData.add(data)
|
} else {
|
checkResult(s)
|
it.refreshAfterCheckResult(data)
|
}
|
}
|
}
|
}
|
lastData = data
|
}
|
|
override fun onDone() {
|
checkResult()
|
}
|
|
/**
|
* 计算一组数据的均值
|
*/
|
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)
|
}
|
}
|
|
/**
|
* 当前数据未出现异常时,或数据循环结束时,判断后续步骤
|
*/
|
private fun checkResult(factor: FactorFilter.SelectedFactor? = null) {
|
val tag = tagMap[factor?.main]
|
if (factor != null && tag != null) {
|
if (tag.existException) {
|
tag.startData?.let {
|
resultList.add(newResult(it, lastData, factor, tag.exceptionData))
|
}
|
tag.existException = false
|
}
|
} else {
|
config.factorFilter.selectedList.forEach { f ->
|
val tag1 = tagMap[f.main] ?: return@forEach
|
if (tag1.existException) {
|
tag1.startData?.let {
|
resultList.add(newResult(it, lastData, f, tag1.exceptionData))
|
}
|
tag1.existException = false
|
}
|
}
|
}
|
|
}
|
|
fun newResult(
|
start: BaseRealTimeData,
|
end: BaseRealTimeData?,
|
factor: FactorFilter.SelectedFactor,
|
exceptionData: List<BaseRealTimeData>,
|
): ExceptionResult {
|
val eType = getExceptionType()
|
return ExceptionResult(start, end, factor, exceptionData, config.mission.missionCode, eType)
|
}
|
}
|