package com.flightfeather.uav.domain.entity
|
|
import com.flightfeather.uav.biz.dataprocess.AvgPair
|
import com.flightfeather.uav.common.utils.DateUtil
|
import com.flightfeather.uav.lightshare.bean.DataVo
|
import com.flightfeather.uav.lightshare.bean.FactorStatistics
|
import com.flightfeather.uav.socket.bean.AirData
|
import com.flightfeather.uav.socket.eunm.FactorType
|
import java.io.Serializable
|
import java.math.BigDecimal
|
import java.time.LocalDateTime
|
import java.time.ZoneId
|
import java.util.*
|
import javax.persistence.Column
|
import javax.persistence.GeneratedValue
|
import javax.persistence.GenerationType
|
import javax.persistence.Id
|
import kotlin.math.atan
|
import kotlin.math.cos
|
import kotlin.math.round
|
import kotlin.math.sin
|
|
/**
|
* 实时监测数据基类
|
*/
|
open class BaseRealTimeData : Serializable {
|
@Id
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
var id: Int? = null
|
|
@Column(name = "device_code")
|
var deviceCode: String? = null
|
|
var latitude: BigDecimal? = null
|
|
var longitude: BigDecimal? = null
|
|
var altitude: Float? = null
|
|
@Column(name = "data_time")
|
var dataTime: Date? = null
|
|
@Column(name = "create_time")
|
var createTime: Date? = null
|
|
@Column(name = "NO2")
|
var no2: Float? = null
|
|
@Column(name = "CO")
|
var co: Float? = null
|
|
@Column(name = "H2S")
|
var h2s: Float? = null
|
|
@Column(name = "SO2")
|
var so2: Float? = null
|
|
@Column(name = "O3")
|
var o3: Float? = null
|
|
@Column(name = "PM25")
|
var pm25: Float? = null
|
|
@Column(name = "PM10")
|
var pm10: Float? = null
|
|
var temperature: Float? = null
|
|
var humidity: Float? = null
|
|
@Column(name = "VOC")
|
var voc: Float? = null
|
|
@Column(name = "NOI")
|
var noi: Float? = null
|
|
@Column(name = "NO")
|
var no: Float? = null
|
|
var velocity: Float? = null
|
|
@Column(name = "wind_speed")
|
var windSpeed: Float? = null
|
|
@Column(name = "wind_direction")
|
var windDirection: Float? = null
|
|
var height: Float? = null
|
|
fun toDataVo() = DataVo().apply {
|
this.time = DateUtil.instance.dateToString(dataTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
|
this.deviceCode = this@BaseRealTimeData.deviceCode
|
this.lng = longitude?.toDouble()
|
this.lat = latitude?.toDouble()
|
this.values = mutableListOf<AirData>().apply {
|
add(AirData().apply { setData(FactorType.NO2, no2) })
|
add(AirData().apply { setData(FactorType.CO, co) })
|
add(AirData().apply { setData(FactorType.H2S, h2s) })
|
add(AirData().apply { setData(FactorType.SO2, so2) })
|
add(AirData().apply { setData(FactorType.O3, o3) })
|
|
add(AirData().apply { setData(FactorType.PM25, pm25) })
|
add(AirData().apply { setData(FactorType.PM10, pm10) })
|
add(AirData().apply { setData(FactorType.TEMPERATURE, temperature) })
|
add(AirData().apply { setData(FactorType.HUMIDITY, humidity) })
|
add(AirData().apply { setData(FactorType.VOC, voc) })
|
|
add(AirData().apply { setData(FactorType.NOI, noi) })
|
add(AirData().apply { setData(FactorType.LNG, lng) })
|
add(AirData().apply { setData(FactorType.LAT, lat) })
|
add(AirData().apply { setData(FactorType.VELOCITY, velocity) })
|
add(AirData().apply {
|
setData(FactorType.TIME, dataTime?.time?.toDouble())
|
statusList = listOf(time ?: "")
|
})
|
add(AirData().apply { setData(FactorType.WIND_SPEED, windSpeed) })
|
add(AirData().apply { setData(FactorType.WIND_DIRECTION, windDirection) })
|
add(AirData().apply { setData(FactorType.HEIGHT, height) })
|
add(AirData().apply { setData(FactorType.NO, no) })
|
}
|
}
|
|
fun getByFactorType(type: FactorType?): Float? {
|
return when (type) {
|
FactorType.NO2 -> no2
|
FactorType.CO -> co
|
FactorType.H2S -> h2s
|
FactorType.SO2 -> so2
|
FactorType.O3 -> o3
|
FactorType.PM25 -> pm25
|
FactorType.PM10 -> pm10
|
FactorType.TEMPERATURE -> temperature
|
FactorType.HUMIDITY -> humidity
|
FactorType.VOC -> voc
|
FactorType.NOI -> noi
|
FactorType.LNG -> longitude?.toFloat()
|
FactorType.LAT -> latitude?.toFloat()
|
FactorType.VELOCITY -> velocity
|
// FactorType.TIME -> dataTime?.time?.toFloat()
|
FactorType.WIND_SPEED -> windSpeed
|
FactorType.WIND_DIRECTION -> windDirection
|
FactorType.HEIGHT -> height
|
FactorType.NO -> no
|
else -> null
|
}
|
}
|
|
}
|
|
fun List<BaseRealTimeData>.avg(onEach: (BaseRealTimeData) -> Unit = { }): BaseRealTimeData {
|
if (isEmpty()) {
|
return BaseRealTimeData()
|
}
|
//风向采用单位矢量法求取均值
|
var u = .0//东西方位分量总和
|
var v = .0//南北方位分量总和
|
var c = 0//风向数据计数
|
|
//除风向外的其他因子采用算术平均法求取均值
|
val tmpList = mutableListOf<AvgPair>()
|
repeat(18) {
|
tmpList.add(AvgPair(0f, 0))
|
}
|
|
forEach {
|
onEach(it)
|
//风向
|
it.windDirection?.let { w ->
|
val r = Math.toRadians(w.toDouble())
|
u += sin(r)
|
v += cos(r)
|
c++
|
}
|
//其余因子
|
tmpList[0].apply {
|
it.latitude?.let {
|
t += it.toFloat()
|
this.c++
|
}
|
}
|
tmpList[1].apply {
|
it.longitude?.let {
|
t += it.toFloat()
|
this.c++
|
}
|
}
|
tmpList[2].apply {
|
it.altitude?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[3].apply {
|
it.no2?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[4].apply {
|
it.co?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[5].apply {
|
it.h2s?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[6].apply {
|
it.so2?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[7].apply {
|
it.o3?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[8].apply {
|
it.pm25?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[9].apply {
|
it.pm10?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[10].apply {
|
it.temperature?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[11].apply {
|
it.humidity?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[12].apply {
|
it.voc?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[13].apply {
|
it.noi?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[14].apply {
|
it.velocity?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[15].apply {
|
it.windSpeed?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[16].apply {
|
it.height?.let {
|
t += it
|
this.c++
|
}
|
}
|
tmpList[17].apply {
|
it.no?.let {
|
t += it
|
this.c++
|
}
|
}
|
}
|
|
return RealTimeDataGridMin().apply {
|
val time = LocalDateTime
|
.ofInstant(get(0).dataTime?.toInstant() ?: Date().toInstant(), ZoneId.systemDefault())
|
.withSecond(0)
|
deviceCode = get(0).deviceCode
|
dataTime = Date.from(time.atZone(ZoneId.systemDefault()).toInstant())
|
createTime = dataTime
|
latitude = tmpList[0].avg().toBigDecimal()
|
longitude = tmpList[1].avg().toBigDecimal()
|
altitude = tmpList[2].avg()
|
no2 = tmpList[3].avg()
|
co = tmpList[4].avg()
|
h2s = tmpList[5].avg()
|
so2 = tmpList[6].avg()
|
o3 = tmpList[7].avg()
|
pm25 = tmpList[8].avg()
|
pm10 = tmpList[9].avg()
|
temperature = tmpList[10].avg()
|
humidity = tmpList[11].avg()
|
voc = tmpList[12].avg()
|
noi = tmpList[13].avg()
|
velocity = tmpList[14].avg()
|
windSpeed = tmpList[15].avg()
|
height = tmpList[16].avg()
|
no = tmpList[17].avg()
|
|
if (c != 0) {
|
val avgU = u / c
|
val avgV = v / c
|
var a = atan(avgU / avgV)
|
a = Math.toDegrees(a)
|
/**
|
* avgU>0;avgV>0: 真实角度处于第一象限,修正值为+0°
|
* avgU>0;avgV<0: 真实角度处于第二象限,修正值为+180°
|
* avgU<0;avgV<0: 真实角度处于第三象限,修正值为+180°
|
* avgU<0;avgV>0: 真实角度处于第四象限,修正值为+360°
|
*/
|
a += if (avgV > 0) {
|
if (avgU > 0) 0 else 360
|
} else {
|
180
|
}
|
windDirection = round(a.toFloat())
|
}
|
}
|
}
|
|
/**
|
* 计算实时监测数据列表的统计信息
|
* 为每种环境因子计算最小值、最大值和平均值
|
*
|
* @return 包含各环境因子统计信息的FactorStatistics列表
|
* 每个FactorStatistics对象包含因子类型、最小值、最大值和平均值
|
*/
|
fun List<BaseRealTimeData>.calDataStatistics(): List<FactorStatistics> {
|
|
// 初始化各环境因子的统计对象列表
|
val statistics = mutableListOf<FactorStatistics>()
|
listOf(
|
FactorType.NO2,
|
FactorType.CO,
|
FactorType.H2S,
|
FactorType.SO2,
|
FactorType.O3,
|
FactorType.PM25,
|
FactorType.PM10,
|
FactorType.VOC,
|
FactorType.NOI,
|
FactorType.VELOCITY,
|
FactorType.WIND_SPEED,
|
FactorType.HEIGHT,
|
FactorType.NO
|
).forEach { statistics.add(FactorStatistics(it)) }
|
|
// 计算平均值并同时更新各因子的最小值和最大值
|
val avgData = avg { item ->
|
// 更新每个因子的最小和最大值
|
statistics[0].updateMinAndMaxValue(item.no2)
|
statistics[1].updateMinAndMaxValue(item.co)
|
statistics[2].updateMinAndMaxValue(item.h2s)
|
statistics[3].updateMinAndMaxValue(item.so2)
|
statistics[4].updateMinAndMaxValue(item.o3)
|
statistics[5].updateMinAndMaxValue(item.pm25)
|
statistics[6].updateMinAndMaxValue(item.pm10)
|
statistics[7].updateMinAndMaxValue(item.voc)
|
statistics[8].updateMinAndMaxValue(item.noi)
|
statistics[9].updateMinAndMaxValue(item.velocity)
|
statistics[10].updateMinAndMaxValue(item.windSpeed)
|
statistics[11].updateMinAndMaxValue(item.height)
|
statistics[12].updateMinAndMaxValue(item.no)
|
}
|
|
// 将计算得到的平均值设置到对应的统计对象中
|
statistics[0].avgValue = avgData.no2 ?: 0f
|
statistics[1].avgValue = avgData.co ?: 0f
|
statistics[2].avgValue = avgData.h2s ?: 0f
|
statistics[3].avgValue = avgData.so2 ?: 0f
|
statistics[4].avgValue = avgData.o3 ?: 0f
|
statistics[5].avgValue = avgData.pm25 ?: 0f
|
statistics[6].avgValue = avgData.pm10 ?: 0f
|
statistics[7].avgValue = avgData.voc ?: 0f
|
statistics[8].avgValue = avgData.noi ?: 0f
|
statistics[9].avgValue = avgData.velocity ?: 0f
|
statistics[10].avgValue = avgData.windSpeed ?: 0f
|
statistics[11].avgValue = avgData.height ?: 0f
|
statistics[12].avgValue = avgData.no ?: 0f
|
|
return statistics
|
}
|