feiyu02
2021-12-30 88ae069fcf657c1790bb04b444e150c06f04e5f6
1. 网格化风向权重分析完善
已修改7个文件
已添加1个文件
676 ■■■■ 文件已修改
src/main/kotlin/com/flightfeather/uav/common/utils/FileExchange.kt 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/dataprocess/ElectricDailyAnalysis.kt 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/bean/ElectricDailyInfo.kt 120 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/bean/QuadrantInfo.kt 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/EPWModelService.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/EPWModelServiceImpl.kt 181 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/ElectricityServiceImpl.kt 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/model/epw/WindDirWeight.kt 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/common/utils/FileExchange.kt
@@ -33,6 +33,8 @@
        val dataList = mutableListOf<RealTimeData>()
        val lastData = mutableListOf<Double>()
        for (i in 1 until sheet.lastRowNum) {
            val row = sheet.getRow(i)
            val time = row.getCell(2).numericCellValue.toLong() * 1000
@@ -44,11 +46,27 @@
            val datetime = Date(time)
            //监测因子
            val jO = JSONObject.parseObject(value)
            val tmp = jO.getDoubleValue(TMP)
            val spC = jO.getDoubleValue(SPCOND)
            val tur = jO.getDoubleValue(TUR)
            val dO = jO.getDoubleValue(DO)
            val ph = jO.getDoubleValue(PH)
            var tmp = jO.getDoubleValue(TMP)
            var spC = jO.getDoubleValue(SPCOND)
            var tur = jO.getDoubleValue(TUR)
            var dO = jO.getDoubleValue(DO)
            var ph = jO.getDoubleValue(PH)
            if (lastData.isEmpty()) {
                lastData.addAll(listOf(tmp, spC, tur, dO, ph))
            } else {
                if (tmp == .0) tmp = lastData[0]
                if (spC == .0) spC = lastData[1]
                if (tur == .0) tur = lastData[2]
                if (dO == .0) dO = lastData[3]
                if (ph == .0) ph = lastData[4]
            }
            lastData[0] = tmp
            lastData[1] = spC
            lastData[2] = tur
            lastData[3] = dO
            lastData[4] = ph
            val factorsList = mutableListOf<AirData>()
@@ -83,6 +101,18 @@
                    factorData = ph
                    physicalQuantity = 0.0
                })
                add(AirData().apply {
                    factorId = "12"
                    factorName = "LNG"
                    factorData = lng
                    physicalQuantity = 0.0
                })
                add(AirData().apply {
                    factorId = "13"
                    factorName = "LAT"
                    factorData = lat
                    physicalQuantity = 0.0
                })
            }
            val factors = JSONObject.toJSON(factorsList).toString()
src/main/kotlin/com/flightfeather/uav/dataprocess/ElectricDailyAnalysis.kt
@@ -19,11 +19,11 @@
        // è¿”回结果
        val result = mutableListOf<ElectricDailyInfo>()
        // æ¯æ—¥ç»Ÿè®¡ä¿¡æ¯
        val dailyInfoMap = mutableMapOf<String, ElectricDailyInfo>()
        val dailyInfoMap = mutableMapOf<Int, ElectricDailyInfo>()
        // 1.数据准备
        val deviceMap = mutableMapOf<String, CompanyDevice>()
        val dataMap = mutableMapOf<String, MutableMap<String, MutableList<ElectricMinuteValue>>>()
        val dataMap = mutableMapOf<String, MutableMap<Int, MutableList<ElectricMinuteValue>>>()
        deviceList.forEach {
            // ç›‘测数据
            if (!dataMap.containsKey(it.cdDeviceCode)) {
@@ -35,7 +35,8 @@
        // 2.轮询数据,统计每日的各项特征
        dataList.forEach {
            // èŽ·å–æ—¥æœŸ
            val day = DateUtil.instance.dateToString(it.mvDataTime, DateUtil.DateStyle.YYYY_MM_DD) ?: ""
            val date = DateUtil.instance.dateToString(it.mvDataTime, DateUtil.DateStyle.YYYYMMDD) ?: ""
            val day = date.toIntOrNull() ?: 0
            // æ¯æ—¥å¯¹åº”一组数据
            if (dataMap[it.mvStatCode]?.containsKey(day) != true) dataMap[it.mvStatCode]?.put(day, mutableListOf())
            val dayList = dataMap[it.mvStatCode]?.get(day)!!
@@ -47,7 +48,8 @@
            // 2.1 æ ¹æ®æ•°æ®åˆ‡æ¢å½“前设备类型
            if (!dailyInfoMap.containsKey(day)) dailyInfoMap[day] = ElectricDailyInfo()
            val dayResult = dailyInfoMap[day]!!
            dayResult.day = day
            dayResult.day = DateUtil.instance.dateToString(it.mvDataTime, DateUtil.DateStyle.YYYY_MM_DD) ?: ""
            dayResult.dayIndex = day
            dayResult.changeType(device)
            // 2.2 æ¯æ—¥èŽ·å–çš„é¦–ä¸ªæ•°æ®ï¼Œå³ä¸ºå½“æ—¥è¯¥è®¾å¤‡å¼€å¯æ—¶é—´
            if (dayList.size == 1) dayResult.setStartTime(it.mvDataTime)
@@ -58,18 +60,41 @@
            // 2.5 ç»Ÿè®¡å½“日设备运行时段(小时)
            val hour = LocalDateTime.ofInstant(it.mvDataTime.toInstant(), ZoneId.systemDefault()).hour
            dayResult.addRunPeriod(hour)
            // 2.6 ç´¯è®¡ç”¨ç”µé‡
            dayResult.addPower(it)
            // 2.7 è®¾å¤‡é¦–次从运行变为待机或关闭状态时,认为是设备的关闭时间
            dayResult.setEndTime(status, it)
        }
        // 2.6 ç»Ÿè®¡å„台设备每日结束时间及分析结果
        // 2.6 ç»Ÿè®¡å„台设备分析结果
        dataMap.forEach { (dCode, dayMap) ->
            dayMap.forEach { (day, list) ->
                dailyInfoMap[day]?.apply {
                    changeType(deviceMap[dCode])
                    setEndTime(list.last().mvDataTime)
                    setEndTime2(list.last().mvDataTime)
                    getResult()
                }
            }
        }
        dailyInfoMap.forEach { (_,v)-> result += v }
        result.sortBy { it.dayIndex }
        for (i in result.indices) {
            if (i > 0) {
                val last = result[i - 1]
                val d = result[i]
                // é’ˆå¯¹ä¸¤ç§è®¾å¤‡ï¼Œå½“昨天的设备未关闭,且今天的开启时间为0点0分内时,认为设备跨日运行
                if (last.plETimeStr == null && d.plRTimeStr?.substring(11, 16) == "00:00") {
                    d.plRTimeStr = null
                    d.rTimeDiff = null
                    d.sResult = true
                }
                if (last.pfETimeStr == null && d.pfRTimeStr?.substring(11, 16) == "00:00") {
                    d.pfRTimeStr = null
                    d.rTimeDiff = null
                    d.sResult = true
                }
            }
        }
        return result
    }
src/main/kotlin/com/flightfeather/uav/lightshare/bean/ElectricDailyInfo.kt
@@ -1,11 +1,13 @@
package com.flightfeather.uav.lightshare.bean
import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.dataprocess.ElectricDailyAnalysis
import com.flightfeather.uav.domain.entity.CompanyDevice
import com.flightfeather.uav.domain.entity.ElectricMinuteValue
import com.flightfeather.uav.lightshare.eunm.ElectricityStatus
import com.flightfeather.uav.lightshare.eunm.ElectricityType
import java.util.*
import kotlin.math.round
/**
 * ç”¨ç”µé‡æ—¥åˆ†æžç»“æžœ
@@ -13,26 +15,33 @@
 * Created by feiyu
 */
class ElectricDailyInfo {
    var day: String = ""
    var day = ""
    var dayIndex = 0
    /***********产线设备ProductionLine, pl************/
    private var plDCode: String? = null  // è®¾å¤‡ç¼–号
    var plDCode: String? = null  // è®¾å¤‡ç¼–号
    private var plSTime: Date? = null    // å¼€å¯æ—¶é—´
    private var plRTime: Date? = null    // æ­£å¼è¿è¡Œæ—¶é—´
    private var plETime: Date? = null    // å…³é—­æ—¶é—´
    private var plRunTime: Int = 0    // è¿è¡Œæ—¶é•¿ï¼ˆåˆ†é’Ÿï¼‰ï¼ˆçŠ¶æ€ä¸ºè¿è¡Œã€é«˜è´Ÿè·çš„æ—¶é•¿ï¼‰
    var plRunTime: Int = 0    // è¿è¡Œæ—¶é•¿ï¼ˆåˆ†é’Ÿï¼‰ï¼ˆçŠ¶æ€ä¸ºè¿è¡Œã€é«˜è´Ÿè·çš„æ—¶é•¿ï¼‰
    private var plRunPeriod = mutableListOf<Int>()// è¿è¡Œæ—¶æ®µï¼ˆå°æ—¶ï¼‰
    var plPower = .0 // ç”¨ç”µé‡ï¼ˆåƒç“¦æ—¶ï¼‰
    /***********净化设备Purify, pf********************/
    private var pfDCode: String? = null
    var pfDCode: String? = null
    private var pfSTime: Date? = null
    private var pfRTime: Date? = null
    private var pfETime: Date? = null
    private var pfRunTime: Int = 0
    var pfRunTime: Int = 0
    private var pfRunPeriod = mutableListOf<Int>()
    var pfPower = .0
    var rTimeDiff:Int = 0 //正式运行时间差
    var plRTimeStr: String? = null
    var plETimeStr: String? = null
    var pfRTimeStr: String? = null
    var pfETimeStr: String? = null
    var rTimeDiff:Int? = 0 //正式运行时间差
    var sResult:Boolean = false //设备开启是否合规
    var eTimeDiff: Int = 0 //关闭时间差
    var eTimeDiff: Int? = 0 //关闭时间差
    var eResult: Boolean = false //设备关闭是否合规
    var runningTimeDiff = 0 //运行时长差
    var rResult:Boolean = false //运行过程是否合规
@@ -63,10 +72,62 @@
        }
    }
    fun setEndTime(date: Date) {
    private var lastPlStatus = -2 // ä¸Šä¸ªæ—¶é—´ç‚¹çš„产线设备状态
    private var plTag = false // true:当日最后一分钟的数据依旧是运行状态,未关闭产线设备
    private var lastPfStatus = -2 // ä¸Šä¸ªæ—¶é—´ç‚¹çš„净化设备状态
    private var pfTag = false
    fun setEndTime(s: Triple<String, String, Double>, e:ElectricMinuteValue?) {
        val hourMinute = DateUtil.instance.dateToString(e?.mvDataTime, DateUtil.DateStyle.HH_MM)
        when (deviceType) {
            ElectricityType.ProductionLine -> plETime = date
            ElectricityType.Purify -> pfETime = date
            ElectricityType.ProductionLine -> {
                if (lastPlStatus == -1) {
                    return
                } else if (lastPlStatus <= ElectricityStatus.A.value) {
                    lastPlStatus = s.first.toInt()
                } else if (lastPlStatus >= ElectricityStatus.B.value) {
                    if (s.first.toInt() <= ElectricityStatus.A.value) {
                        plETime = e?.mvDataTime
                        lastPlStatus = -1
                    } else {
                        if (hourMinute == "23:59") plTag = true
                        lastPlStatus = s.first.toInt()
                    }
                }
            }
            ElectricityType.Purify -> {
                if (lastPfStatus == -1) {
                    return
                } else if (lastPfStatus <= ElectricityStatus.A.value) {
                    lastPfStatus = s.first.toInt()
                } else if (lastPfStatus >= ElectricityStatus.B.value) {
                    if (s.first.toInt() <= ElectricityStatus.A.value) {
                        pfETime = e?.mvDataTime
                        lastPfStatus = -1
                    } else {
                        if (hourMinute == "23:59") pfTag = true
                        lastPfStatus = s.first.toInt()
                    }
                }
            }
            else -> Unit
        }
    }
    /**
     * å¦‚果设备关闭时是直接从运行状态断电,则在此处将最后一个数据的时间记为关闭时间
     */
    fun setEndTime2(date: Date) {
        when (deviceType) {
            ElectricityType.ProductionLine -> {
                if (plETime == null && !plTag) {
                    plETime = date
                }
            }
            ElectricityType.Purify -> {
                if (pfETime == null && !pfTag) {
                    pfETime = date
                }
            }
            else -> Unit
        }
    }
@@ -119,20 +180,49 @@
    }
    /**
     * ç´¯è®¡ç”¨ç”µé‡
     */
    fun addPower(e: ElectricMinuteValue?) {
        e?.let {
            val avgElectric = (it.mvElectricityA + it.mvElectricityB + it.mvElectricityC) / 3
            val power = avgElectric * 1 / 60
            when (deviceType) {
                ElectricityType.ProductionLine -> plPower += power
                ElectricityType.Purify -> pfPower += power
                else -> Unit
            }
        }
    }
    /**
     * ç»Ÿè®¡å¾—出当日分析结果
     */
    fun getResult() {
        // TODO: 2021/12/1 1. è®¾å¤‡æ­£å¼è¿è¡Œæ—¶é—´å·® =》开启是否合规
        // ç”¨ç”µé‡
        plPower = round(plPower * 100) / 100
        pfPower = round(pfPower * 100) / 100
        // å¼€å…³æ—¶é—´æ ¼å¼åŒ–
        plRTimeStr = DateUtil.instance.dateToString(plRTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        plETimeStr = DateUtil.instance.dateToString(plETime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        pfRTimeStr = DateUtil.instance.dateToString(pfRTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        pfETimeStr = DateUtil.instance.dateToString(pfETime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        // 1. è®¾å¤‡æ­£å¼è¿è¡Œæ—¶é—´å·® =》开启是否合规
        if (pfRTime != null && plRTime != null) {
            rTimeDiff = ((pfRTime!!.time - plRTime!!.time) / 1000 / 60).toInt()
            sResult = rTimeDiff < 0
            sResult = rTimeDiff == null || rTimeDiff!! < 0
        }
        // TODO: 2021/12/1 2,设备关闭时间差 =》关闭是否合规
        // 2,设备关闭时间差 =》关闭是否合规
        if (pfETime != null && plETime != null) {
            eTimeDiff = ((pfETime!!.time - plETime!!.time) / 1000 / 60).toInt()
            eResult = eTimeDiff > 0
            eResult = eTimeDiff == null || eTimeDiff!! > 0
        } else {
            // å½“关闭时间存在null时,说明当日并没有关闭设备,关闭合规
            eTimeDiff = null
            eResult = true
        }
        // TODO: 2021/12/1 3. è¿è¡Œæ—¶é•¿å·® =》运行过程是否合规
        // 3. è¿è¡Œæ—¶é•¿å·® =》运行过程是否合规
        runningTimeDiff = pfRunTime - plRunTime
        rResult = runningTimeDiff > 0
src/main/kotlin/com/flightfeather/uav/lightshare/bean/QuadrantInfo.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package com.flightfeather.uav.lightshare.bean
import com.flightfeather.uav.common.utils.MapUtil
import kotlin.math.PI
/**
 * ç½‘格化权重分析象限对象
 * ä»¥ç›‘测点为网格线的中心的基础上,向外扩散的每一圈网格按照坐标系分为四个象限
 * æ¯ä¸ªè±¡é™ä¸­çš„网格按照角上的网格作为分界,分成3部分,分别有不同的向外扩散机制
 */
class QuadrantInfo{
    val gridsHorizontal = mutableListOf<GridVo>()
    val gridsCorner = mutableListOf<GridVo>()
    val gridsVertical = mutableListOf<GridVo>()
    fun new(type: Quadrant, turns: Int, lng: Double, lat: Double, dx: Double, dy: Double) {
        var plusMinusX = 1 // ç»åº¦å¢žåŠ æˆ–å‡å°‘
        var plusMinusY = 1 // çº¬åº¦å¢žåŠ æˆ–å‡å°‘
        when (type) {
            Quadrant.First -> {
                plusMinusX = 1
                plusMinusY = 1
            }
            Quadrant.Second -> {
                plusMinusX = 1
                plusMinusY = -1
            }
            Quadrant.Third -> {
                plusMinusX = -1
                plusMinusY = -1
            }
            Quadrant.Fourth -> {
                plusMinusX = -1
                plusMinusY = 1
            }
        }
        // è§’
        gridsCorner.add(GridVo("$turns-${type.value}-c").apply {
            lb = Pair(lng + turns * dx * plusMinusX, lat + turns * dy * plusMinusY)
            rt = Pair(lb!!.first + dx, lb!!.second + dy)
            ciLongitude = (lb!!.first + dx / 2).toBigDecimal()
            ciLatitude = (lb!!.second + dy / 2).toBigDecimal()
        })
        // æ°´å¹³æ–¹å‘
        repeat(turns) {
            gridsHorizontal.add(GridVo("$turns-${type.value}-l-$it").apply {
                lb = Pair(lng + it * dx * plusMinusX, lat + turns * dy * plusMinusY)
                rt = Pair(lb!!.first + dx, lb!!.second + dy)
                ciLongitude = (lb!!.first + dx / 2).toBigDecimal()
                ciLatitude = (lb!!.second + dy / 2).toBigDecimal()
            })
        }
        // åž‚直方向
        repeat(turns) {
            gridsVertical.add(GridVo("$turns-${type.value}-l-$it").apply {
                lb = Pair(lng + turns * dx * plusMinusX, lat + it * dy * plusMinusY)
                rt = Pair(lb!!.first + dx, lb!!.second + dy)
                ciLongitude = (lb!!.first + dx / 2).toBigDecimal()
                ciLatitude = (lb!!.second + dy / 2).toBigDecimal()
            })
        }
    }
    fun next() {
    }
}
enum class Quadrant(val des: String, val value:Int){
    First("右上象限", 1),
    Second("右下象限", 2),
    Third("左下象限", 3),
    Fourth("左上象限", 4),
}
object GridFactory {
    // ç›‘测点坐标
    private var lng = .0
    private var lat = .0
    private var dx = .0
    private var dy = .0
    /**
     * è®¾ç½®ç›‘测点坐标
     */
    fun setCenter(lng: Double, lat: Double): GridFactory {
        this.lng = lng
        this.lat = lat
        return this
    }
    /**
     * è®¾ç½®ç½‘格长度
     * @param len ç½‘格长度(米)
     */
    fun setGridLength(len: Double): GridFactory {
        val center = Pair(lng, lat)
        val p1 = MapUtil.getPointByLen(center, len, PI / 2)//正东方向(90°)的坐标点
        val p2 = MapUtil.getPointByLen(center, len, PI)//正南方向(180°)的坐标点
        this.dx = p1.first - center.first
        this.dy = center.second - p2.second
        return this
    }
    /**
     * æ–°å»ºä¸€åœˆç½‘æ ¼
     * @param turns åœˆæ•°ï¼ˆä»Ž0开始)
     */
//    fun newGridsByTurns(turns: Int): List<QuadrantInfo> {
//
//    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/EPWModelService.kt
@@ -7,10 +7,18 @@
interface EPWModelService {
    /**
     * ç½‘格分析分析
     * ç½‘格分析
     * æŒ‰ç…§å›ºå®šçš„æ€»é¢ç§¯ï¼Œæ ¹æ®ç»™å®šçš„网格边长,动态生成对应数量的网格
     * @param type æ•°æ®æºç±»åž‹ @see [UWDeviceType], 0a,0b,0c,0d
     * @param len ç½‘格长度
     */
    fun getEpwModelResult(deviceCode: String, startTime: String, endTime: String, len: Double): BaseResponse<List<GridVo>>
    /**
     * åŠ¨æ€ç½‘æ ¼åˆ†æž
     * ç›®å‰åªé’ˆå¯¹å®šç‚¹ç›‘测
     * å¯è‡ªåŠ¨è®¡ç®—å‡ºæ‰€æœ‰çš„é«˜é£Žé™©åŠä¸­é£Žé™©åŒºåŸŸ
     */
    fun getEpwModelResultDynamic(deviceCode: String, startTime: String, endTime: String, len: Double): BaseResponse<List<GridVo>>
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/EPWModelServiceImpl.kt
@@ -3,20 +3,22 @@
import com.flightfeather.uav.common.GridLat
import com.flightfeather.uav.common.GridLng
import com.flightfeather.uav.common.utils.MapUtil
import com.flightfeather.uav.lightshare.bean.BaseResponse
import com.flightfeather.uav.lightshare.bean.GridVo
import com.flightfeather.uav.lightshare.bean.*
import com.flightfeather.uav.lightshare.service.EPWModelService
import com.flightfeather.uav.lightshare.service.RealTimeDataService
import com.flightfeather.uav.model.epw.EPWGridModel
import com.flightfeather.uav.model.epw.EPWModel
import com.flightfeather.uav.socket.eunm.UWDeviceType
import org.springframework.stereotype.Service
import kotlin.math.PI
import kotlin.math.*
@Service
class EPWModelServiceImpl(
    private val realTimeDataService: RealTimeDataService,
) : EPWModelService {
    companion object {
        private const val LEN = 3// æ ¹æ®é£Žå‘网格向外拓展圈数
    }
    val epwModel = EPWGridModel()
@@ -34,6 +36,60 @@
        if (gridType == 'f') return BaseResponse(false)
        val points = mutableListOf<GridVo>()
        // èŽ·å–å…¨éƒ¨æ•°æ®è®¡ç®—å¹³å‡é£Žå‘
        val dataList = mutableListOf<DataVo>()
        var page = 1
        var totalPage = -1
        //风向采用单位矢量法求取均值
        var u = .0//东西方位分量总和
        var v = .0//南北方位分量总和
        var c = 0//风向数据计数
        var windDirection = .0 // å¹³å‡é£Žå‘角度
        while (totalPage == -1 || page <= totalPage) {
            realTimeDataService.getSecondData(deviceCode, startTime, endTime, 0, page, 5000).apply {
                if (totalPage == -1) {
                    totalPage = head?.totalPage ?: 0
                }
                val list = data ?: emptyList()
                // FIXME: 2021/7/13 æ­¤å¤„为了测试暂时将站点经纬度写死,后续通过数据库配置获取
                list.forEach {
                    if (it.lng == 0.0 && it.lat == 0.0) {
                        it.lng = GridLng
                        it.lat = GridLat
                    }
                    val r = Math.toRadians(it.values?.get(16)?.factorData ?: .0)
                    u += sin(r)
                    v += cos(r)
                    c++
                }
                dataList.addAll(list)
                page++
            }
        }
        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 = a
        }
        // æ ¹æ®ä¸åŒç±»åž‹ï¼Œç¡®å®šä¸åŒçš„网格生成方式,得出网格中心点集合(网格默认采用正方形)
        // èµ°èˆªç›‘测
        if (gridType == '0') {
@@ -42,26 +98,51 @@
        // å®šç‚¹ç›‘测
        else if (gridType == '1') {
            // FIXME: 2021/12/6 æ­¤å¤„为了测试暂时将站点经纬度写死,后续通过数据库配置获取
            val center = Pair(121.235813, 30.835898)
            val center = Pair(GridLng, GridLat)
            // a.确定网格长度对应的坐标差值
            // FIXME: 2021/12/16 æ­¤å¤„网格坐标的计算比较简单,近似将其当作平面坐标来做,后续可改进
            val p1 = MapUtil.getPointByLen(center, len, PI / 2)//正东方向(90°)的坐标点
            val p2 = MapUtil.getPointByLen(center, len, PI)//正南方向(180°)的坐标点
            val dx = p1.first - center.first
            val dy = center.second - p2.second
            // b.确定单边有多少个网格(规定监测点在中心网格的中点上,因此单边网格数一定为奇数)
            val totalLen = 2000 // ç½‘格范围,边长为20千米的正方形
            val totalLen = 2000 // FIXME: 2021/12/16 ç½‘格范围,边长为20千米的正方形
            val gridNum = ((totalLen / 2 / len).toInt() - 1) * 2 + 1
            // c.确定左上角网格左下和右上的两个对角点坐标
            var len1 = gridNum//水平方向网格数
            var width = gridNum//垂直方向网格数
            //中心点坐标
            val g1CenterLng = center.first - (gridNum - 1) / 2 * dx//经度减小
            val g1CenterLat = center.second + (gridNum - 1) / 2 * dy//纬度增加
            var g1CenterLng = center.first - (gridNum - 1) / 2 * dx//经度减小
            var g1CenterLat = center.second + (gridNum - 1) / 2 * dy//纬度增加
            when (windDirection) {
                in .0..90.0 -> {
                    g1CenterLat += LEN * dy
                    width += LEN
                    len1 += LEN
                }
                in 90.0..180.0 -> {
                    len1 += LEN
                    width += LEN
                }
                in 180.0..270.0 -> {
                    g1CenterLng -= LEN * dx
                    len1 += LEN
                    width += LEN
                }
                in 270.0..360.0 -> {
                    g1CenterLng -= LEN * dx
                    g1CenterLat += LEN * dy
                    len1 += LEN
                    width += LEN
                }
            }
            //左下坐标
            val g1LB = Pair(g1CenterLng - dx / 2, g1CenterLat - dy / 2)
            //右上坐标
            val g1RT = Pair(g1CenterLng + dx / 2, g1CenterLat + dy / 2)
            // d.得出所有网格的两个对角点坐标
            for (x in 0 until gridNum) {
                for (y in 0 until gridNum) {
            for (x in 0 until len1) {
                for (y in 0 until width) {
                    points.add(GridVo("$x-$y").apply {
                        this.lb = Pair(g1LB.first + dx * x, g1LB.second - dy * y)
                        this.rt = Pair(g1RT.first + dx * x, g1RT.second - dy * y)
@@ -72,27 +153,7 @@
            }
        }
        // è®¡ç®—各中心点污染风险权重结果并赋予对应影响等级
        var page = 1
        var totalPage = -1
        while (totalPage == -1 || page <= totalPage) {
            realTimeDataService.getSecondData(deviceCode, startTime, endTime, 0, page, 5000).apply {
                if (totalPage == -1) {
                    totalPage = head?.totalPage ?: 0
                }
                val dataList = data ?: emptyList()
                // FIXME: 2021/7/13 æ­¤å¤„为了测试暂时将站点经纬度写死,后续通过数据库配置获取
                dataList.forEach {
                    if (it.lng == 0.0 && it.lat == 0.0) {
                        it.lng = GridLng
                        it.lat = GridLat
                    }
                }
                epwModel.execute(dataList, points, true)
                page++
            }
        }
        epwModel.execute(dataList, points, true)
        val r = epwModel.outputResult()
        val max = mutableMapOf<String, Double>()//记录每种监测因子的最大值
@@ -138,4 +199,62 @@
        }
        return BaseResponse(true, data = points)
    }
    /**
     * ä»¥ç›‘测点为网格线上的中心,向外按照正方形进行扩散。
     * é¦–先默认展示监测点周围一圈16个网格,然后再以外圈12个网格为基础向外扩散,每次最多扩散一圈,判断权重结果,保留所有的高风险和中风险网格,并保留两圈低风险网格
     * å…¶ä¸­çš„高、中、低风险判断依据为网格权重值对应当前最大值的比例(分界为66.66%和33.33%)
     */
    override fun getEpwModelResultDynamic(deviceCode: String, startTime: String, endTime: String, len: Double): BaseResponse<List<GridVo>> {
//        if (deviceCode.length < 2) return BaseResponse(false, "设备编号格式错误")
//        // ç¡®å®šæ•°æ®æºç±»åž‹ï¼ŒåŒºåˆ†ä¸ºâ€˜å®šç‚¹ç›‘测数据’和‘移动监测数据两种’
//        val gridType = when (deviceCode.substring(0, 2)) {
//            UWDeviceType.UAV.value -> '0'
//            UWDeviceType.VEHICLE.value -> '0'
//            UWDeviceType.GRID.value -> '1'
//            UWDeviceType.BOAT.value -> 'f'
//            else -> 'f'
//        }
//        if (gridType == 'f' || gridType == '0') return BaseResponse(false, "该设备类型不支持动态网格风险计算,只有定点网格化设备可行")
//        val points = mutableListOf<GridVo>()
//        // FIXME: 2021/12/6 æ­¤å¤„为了测试暂时将站点经纬度写死,后续通过数据库配置获取
//        val center = Pair(GridLng, GridLat)
//
//        // a.确定网格长度对应的坐标差值
//        // FIXME: 2021/12/16 æ­¤å¤„网格坐标的计算比较简单,近似将其当作平面坐标来做,后续可改进
//        val p1 = MapUtil.getPointByLen(center, len, PI / 2)//正东方向(90°)的坐标点
//        val p2 = MapUtil.getPointByLen(center, len, PI)//正南方向(180°)的坐标点
//        val dx = p1.first - center.first
//        val dy = center.second - p2.second
//
//        val grids = mutableListOf<QuadrantInfo>()// æ‰€æœ‰ç½‘æ ¼
//        val outerMost = mutableMapOf<Quadrant, QuadrantInfo>()// å½“前最外圈的网格
//        // b.先计算内部两圈的结果
//        grids.addAll(listOf(
//            // ç¬¬ä¸€åœˆ
//            QuadrantInfo(Quadrant.First, 0),
//            QuadrantInfo(Quadrant.Second, 0),
//            QuadrantInfo(Quadrant.Third, 0),
//            QuadrantInfo(Quadrant.Fourth, 0),
//            // ç¬¬äºŒåœˆ
//            QuadrantInfo(Quadrant.First, 1),
//            QuadrantInfo(Quadrant.Second, 1),
//            QuadrantInfo(Quadrant.Third, 1),
//            QuadrantInfo(Quadrant.Fourth, 1),
//        ))
//        //当前最外圈(第二圈)
//        outerMost[Quadrant.First] = grids[4]
//        outerMost[Quadrant.Second] = grids[5]
//        outerMost[Quadrant.Third] = grids[6]
//        outerMost[Quadrant.Fourth] = grids[7]
//        //中心点坐标
//        val g1CenterLng = center.first - (gridNum - 1) / 2 * dx//经度减小
//        val g1CenterLat = center.second + (gridNum - 1) / 2 * dy//纬度增加
//        //左下坐标
//        val g1LB = Pair(g1CenterLng - dx / 2, g1CenterLat - dy / 2)
//        //右上坐标
//        val g1RT = Pair(g1CenterLng + dx / 2, g1CenterLat + dy / 2)
        return BaseResponse(true)
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/ElectricityServiceImpl.kt
@@ -189,26 +189,20 @@
        // æ ¹æ®ä¸¤å°è®¾å¤‡çš„æœ€é•¿èµ·å§‹æ—¶é—´ï¼Œè®¡ç®—其中每一分钟的对应均值
        while (!lsT.isAfter(leT)) {
            // ä¸¤å°è®¾å¤‡çš„æ•°æ®æ ¹æ®æ—¶é—´åˆå¹¶ä¸ºä¸€ä¸ªç»“构体
            val vo = ElectricVo(lsT.format(dateFormatter2))
            // FIXME: 2021/11/22 æ­¤å¤„由于前端设备的采样时间不标准 ï¼Œé‡‡æ ·å‘¨æœŸå¹¶ä¸æ˜¯ä¸¥æ ¼çš„1分钟,导致采样时间有时会缺少1分钟的数据
            // FIXME: 2021/11/22 å› æ­¤ï¼Œå½“某一分钟该设备数据轮空时,采用前一个数据作为填充
            if (dataList1.isNotEmpty()) {
                val d = dataList1[0]
                val t = LocalDateTime.ofInstant(d?.mvDataTime?.toInstant(), ZoneId.systemDefault()).withSecond(0)
                if (lsT.isEqual(t)) {
                    vo.apply {
                        d1eA = d?.mvElectricityA ?: .0
                        d1eB = d?.mvElectricityB ?: .0
                        d1eC = d?.mvElectricityC ?: .0
                        val s = ElectricDailyAnalysis.getStatus(d, d1)
                        d1Status = s.first
                        d1StatusName = s.second
                        d1Avg = s.third
                    }
                    dataList1.removeAt(0)
                } else {
            val data1 = if (dataList1.isNotEmpty()) dataList1[0] else null
            val t1 = data1?.let {  LocalDateTime.ofInstant(it.mvDataTime?.toInstant(), ZoneId.systemDefault()).withSecond(0) }
            val data2 = if (dataList2.isNotEmpty()) dataList2[0] else null
            val t2 = data2?.let { LocalDateTime.ofInstant(it.mvDataTime?.toInstant(), ZoneId.systemDefault()).withSecond(0) }
            if ((data1 == null || !lsT.isEqual(t1))
                && (data2 == null || !lsT.isEqual(t2))
            ) {
            } else {
                // ä¸¤å°è®¾å¤‡çš„æ•°æ®æ ¹æ®æ—¶é—´åˆå¹¶ä¸ºä¸€ä¸ªç»“构体
                val vo = ElectricVo(lsT.format(dateFormatter2))
                if (data1 == null || !lsT.isEqual(t1)) {
                    result.lastOrNull()?.let {
                        vo.apply {
                            d1eA = it.d1eA
@@ -219,34 +213,20 @@
                            d1Avg = it.d1Avg
                        }
                    }
                }
            } else {
                result.lastOrNull()?.let {
                    vo.apply {
                        d1eA = it.d1eA
                        d1eB = it.d1eB
                        d1eC = it.d1eC
                        d1Status = it.d1Status
                        d1StatusName = it.d1StatusName
                        d1Avg = it.d1Avg
                    }
                }
            }
            if (dataList2.isNotEmpty()) {
                val d = dataList2[0]
                val t = LocalDateTime.ofInstant(d?.mvDataTime?.toInstant(), ZoneId.systemDefault()).withSecond(0)
                if (lsT.isEqual(t)) {
                    vo.apply {
                        d2eA = d?.mvElectricityA ?: .0
                        d2eB = d?.mvElectricityB ?: .0
                        d2eC = d?.mvElectricityC ?: .0
                        val s = ElectricDailyAnalysis.getStatus(d, d2)
                        d2Status = s.first
                        d2StatusName = s.second
                        d2Avg = s.third
                    }
                    dataList2.removeAt(0)
                } else {
                    vo.apply {
                        d1eA = data1.mvElectricityA ?: .0
                        d1eB = data1.mvElectricityB ?: .0
                        d1eC = data1.mvElectricityC ?: .0
                        val s = ElectricDailyAnalysis.getStatus(data1, d1)
                        d1Status = s.first
                        d1StatusName = s.second
                        d1Avg = s.third
                    }
                    dataList1.removeAt(0)
                }
                if (data2 == null || !lsT.isEqual(t2)) {
                    result.lastOrNull()?.let {
                        vo.apply {
                            d2eA = it.d2eA
@@ -257,20 +237,97 @@
                            d2Avg = it.d2Avg
                        }
                    }
                }
            } else {
                result.lastOrNull()?.let {
                } else {
                    vo.apply {
                        d2eA = it.d2eA
                        d2eB = it.d2eB
                        d2eC = it.d2eC
                        d2Status = it.d2Status
                        d2StatusName = it.d2StatusName
                        d2Avg = it.d2Avg
                        d2eA = data2.mvElectricityA ?: .0
                        d2eB = data2.mvElectricityB ?: .0
                        d2eC = data2.mvElectricityC ?: .0
                        val s = ElectricDailyAnalysis.getStatus(data2, d2)
                        d2Status = s.first
                        d2StatusName = s.second
                        d2Avg = s.third
                    }
                    dataList2.removeAt(0)
                }
                result.add(vo)
            }
            result.add(vo)
//            if (dataList1.isNotEmpty()) {
//                val d = dataList1[0]
//                val t = LocalDateTime.ofInstant(d?.mvDataTime?.toInstant(), ZoneId.systemDefault()).withSecond(0)
//                if (lsT.isEqual(t)) {
//                    vo.apply {
//                        d1eA = d?.mvElectricityA ?: .0
//                        d1eB = d?.mvElectricityB ?: .0
//                        d1eC = d?.mvElectricityC ?: .0
//                        val s = ElectricDailyAnalysis.getStatus(d, d1)
//                        d1Status = s.first
//                        d1StatusName = s.second
//                        d1Avg = s.third
//                    }
//                    dataList1.removeAt(0)
//                } else {
//                    result.lastOrNull()?.let {
//                        vo.apply {
//                            d1eA = it.d1eA
//                            d1eB = it.d1eB
//                            d1eC = it.d1eC
//                            d1Status = it.d1Status
//                            d1StatusName = it.d1StatusName
//                            d1Avg = it.d1Avg
//                        }
//                    }
//                }
//            } else {
//                result.lastOrNull()?.let {
//                    vo.apply {
//                        d1eA = it.d1eA
//                        d1eB = it.d1eB
//                        d1eC = it.d1eC
//                        d1Status = it.d1Status
//                        d1StatusName = it.d1StatusName
//                        d1Avg = it.d1Avg
//                    }
//                }
//            }
//            if (dataList2.isNotEmpty()) {
//                val d = dataList2[0]
//                val t = LocalDateTime.ofInstant(d?.mvDataTime?.toInstant(), ZoneId.systemDefault()).withSecond(0)
//                if (lsT.isEqual(t)) {
//                    vo.apply {
//                        d2eA = d?.mvElectricityA ?: .0
//                        d2eB = d?.mvElectricityB ?: .0
//                        d2eC = d?.mvElectricityC ?: .0
//                        val s = ElectricDailyAnalysis.getStatus(d, d2)
//                        d2Status = s.first
//                        d2StatusName = s.second
//                        d2Avg = s.third
//                    }
//                    dataList2.removeAt(0)
//                } else {
//                    result.lastOrNull()?.let {
//                        vo.apply {
//                            d2eA = it.d2eA
//                            d2eB = it.d2eB
//                            d2eC = it.d2eC
//                            d2Status = it.d2Status
//                            d2StatusName = it.d2StatusName
//                            d2Avg = it.d2Avg
//                        }
//                    }
//                }
//            } else {
//                result.lastOrNull()?.let {
//                    vo.apply {
//                        d2eA = it.d2eA
//                        d2eB = it.d2eB
//                        d2eC = it.d2eC
//                        d2Status = it.d2Status
//                        d2StatusName = it.d2StatusName
//                        d2Avg = it.d2Avg
//                    }
//                }
//            }
            lsT = lsT.plusMinutes(1)
        }
@@ -301,10 +358,10 @@
        // å½“有开始结束时间时,判断格式是否正确
        else {
            try {
                st = LocalDateTime.parse(startTime, dateFormatter3).withHour(0).withMinute(0).withSecond(0)
                et = LocalDateTime.parse(endTime, dateFormatter3).withHour(23).withMinute(59).withSecond(59)
                st = LocalDateTime.parse("$startTime 00:00:00", dateFormatter3)
                et = LocalDateTime.parse("$endTime 23:59:59", dateFormatter3)
            } catch (e: DateTimeParseException) {
                return BaseResponse(false, "时间格式错误,应为yyyy-MM-dd hh:mm:dd")
                return BaseResponse(false, "时间格式错误,应为yyyy-MM-dd")
            }
        }
src/main/kotlin/com/flightfeather/uav/model/epw/WindDirWeight.kt
@@ -39,6 +39,7 @@
        x1 = abs(x2 - x1)
        if (x1 > 180) x1 = 360 - x1
//        println("夹角:$x1")
        x1 = 180 - x1
        return x1
    }
}