feat: 新增姿态平滑
This commit is contained in:
parent
cbb1a22161
commit
3a503701af
@ -1,6 +1,7 @@
|
||||
package com.imdroid.beidou.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.imdroid.beidou.service.CommonExcelService;
|
||||
import com.imdroid.secapi.dto.GnssStatusMsg;
|
||||
@ -11,12 +12,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 状态消息 控制器
|
||||
@ -32,6 +34,10 @@ public class GnssMsgStatusController extends BasicController implements CommonEx
|
||||
GnssStatusMsgMapper statusMsgMapper;
|
||||
@Autowired
|
||||
OpLogManager opLogManager;
|
||||
|
||||
// 滑动窗口大小
|
||||
private static final int WINDOW_SIZE = 5;
|
||||
|
||||
@RequestMapping("/page/gnss_msg_status")
|
||||
public String gnssStatusMsg(Model m, HttpSession session) {
|
||||
initModel(m, session);
|
||||
@ -54,6 +60,118 @@ public class GnssMsgStatusController extends BasicController implements CommonEx
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特定设备的数据,并应用滑动平均
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param limit 获取条数,默认150条
|
||||
* @return 处理后的数据
|
||||
*/
|
||||
@RequestMapping("/gnss/msg/status/device_data")
|
||||
@ResponseBody
|
||||
public JSONObject getDeviceData(@RequestParam String deviceId,
|
||||
@RequestParam(required = false, defaultValue = "150") Integer limit) {
|
||||
// 查询指定设备的最近数据
|
||||
QueryWrapper<GnssStatusMsg> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("deviceid", deviceId)
|
||||
.orderByDesc("createtime")
|
||||
.last("limit " + limit);
|
||||
|
||||
List<GnssStatusMsg> dataList = statusMsgMapper.selectList(queryWrapper);
|
||||
// 反转列表,使其按时间升序排列
|
||||
Collections.reverse(dataList);
|
||||
|
||||
// 应用滑动平均
|
||||
List<GnssStatusMsg> smoothedData = applyMovingAverage(dataList, WINDOW_SIZE);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("code", 0);
|
||||
result.put("msg", "");
|
||||
result.put("count", smoothedData.size());
|
||||
result.put("data", smoothedData);
|
||||
result.put("originalData", dataList);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用滑动平均算法
|
||||
*
|
||||
* @param dataList 原始数据列表
|
||||
* @param windowSize 窗口大小
|
||||
* @return 处理后的数据列表
|
||||
*/
|
||||
private List<GnssStatusMsg> applyMovingAverage(List<GnssStatusMsg> dataList, int windowSize) {
|
||||
if (dataList.size() <= 1) {
|
||||
return new ArrayList<>(dataList);
|
||||
}
|
||||
|
||||
List<GnssStatusMsg> result = new ArrayList<>(dataList.size());
|
||||
|
||||
// 初始窗口
|
||||
Deque<Float> rollWindow = new LinkedList<>();
|
||||
Deque<Float> pitchWindow = new LinkedList<>();
|
||||
Deque<Float> yawWindow = new LinkedList<>();
|
||||
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
GnssStatusMsg currentMsg = dataList.get(i);
|
||||
GnssStatusMsg smoothedMsg = new GnssStatusMsg();
|
||||
|
||||
// 复制基本属性
|
||||
smoothedMsg.setId(currentMsg.getId());
|
||||
smoothedMsg.setDeviceid(currentMsg.getDeviceid());
|
||||
smoothedMsg.setCreatetime(currentMsg.getCreatetime());
|
||||
smoothedMsg.setDevicetime(currentMsg.getDevicetime());
|
||||
smoothedMsg.setTenantid(currentMsg.getTenantid());
|
||||
smoothedMsg.setDtustate(currentMsg.getDtustate());
|
||||
smoothedMsg.setRssi(currentMsg.getRssi());
|
||||
smoothedMsg.setVoltage(currentMsg.getVoltage());
|
||||
smoothedMsg.setSolarvoltage(currentMsg.getSolarvoltage());
|
||||
smoothedMsg.setChargecurrency(currentMsg.getChargecurrency());
|
||||
smoothedMsg.setChargewatt(currentMsg.getChargewatt());
|
||||
smoothedMsg.setTemperature(currentMsg.getTemperature());
|
||||
smoothedMsg.setHumidity(currentMsg.getHumidity());
|
||||
|
||||
// 添加当前值到窗口
|
||||
if (currentMsg.getRoll() != null) rollWindow.addLast(currentMsg.getRoll());
|
||||
if (currentMsg.getPitch() != null) pitchWindow.addLast(currentMsg.getPitch());
|
||||
if (currentMsg.getYaw() != null) yawWindow.addLast(currentMsg.getYaw());
|
||||
|
||||
// 保持窗口大小
|
||||
if (rollWindow.size() > windowSize) rollWindow.removeFirst();
|
||||
if (pitchWindow.size() > windowSize) pitchWindow.removeFirst();
|
||||
if (yawWindow.size() > windowSize) yawWindow.removeFirst();
|
||||
|
||||
// 计算平均值
|
||||
if (!rollWindow.isEmpty()) {
|
||||
float rollSum = 0;
|
||||
for (Float val : rollWindow) rollSum += val;
|
||||
smoothedMsg.setRoll(rollSum / rollWindow.size());
|
||||
} else {
|
||||
smoothedMsg.setRoll(currentMsg.getRoll());
|
||||
}
|
||||
|
||||
if (!pitchWindow.isEmpty()) {
|
||||
float pitchSum = 0;
|
||||
for (Float val : pitchWindow) pitchSum += val;
|
||||
smoothedMsg.setPitch(pitchSum / pitchWindow.size());
|
||||
} else {
|
||||
smoothedMsg.setPitch(currentMsg.getPitch());
|
||||
}
|
||||
|
||||
if (!yawWindow.isEmpty()) {
|
||||
float yawSum = 0;
|
||||
for (Float val : yawWindow) yawSum += val;
|
||||
smoothedMsg.setYaw(yawSum / yawWindow.size());
|
||||
} else {
|
||||
smoothedMsg.setYaw(currentMsg.getYaw());
|
||||
}
|
||||
|
||||
result.add(smoothedMsg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">设备号</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="s_deviceid" autocomplete="off" class="layui-input">
|
||||
<input type="text" name="s_deviceid" id="deviceid" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
@ -44,7 +44,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button type="submit" class="layui-btn layui-btn-primary" lay-submit lay-filter="data-search-btn"><i class="layui-icon"></i> 搜 索</button>
|
||||
<button type="submit" class="layui-btn layui-btn-primary" lay-submit lay-filter="data-search-btn"><i class="layui-icon"></i> 搜 索</button>
|
||||
<button type="submit" class="layui-btn layui-btn-primary" lay-submit lay-filter="data-export-btn"><i class="layui-icon"></i>导出</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -52,17 +52,36 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="layui-tab layui-tab-card" lay-filter="data-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">数据表格</li>
|
||||
<li>姿态曲线</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<table class="layui-hide" id="currentTableId" lay-filter="currentTableFilter"></table>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div id="echarts-attitude" style="min-height:500px;padding: 10px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="../js/lay-module/echarts/echartsTheme.js" charset="utf-8"></script>
|
||||
<script src="../js/lay-module/echarts/echarts.js" charset="utf-8"></script>
|
||||
<script src="../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
|
||||
<script th:inline="javascript">
|
||||
layui.use(['form', 'table', 'laydate'], function () {
|
||||
layui.use(['form', 'table', 'laydate', 'element'], function () {
|
||||
var $ = layui.$;
|
||||
var form = layui.form,
|
||||
table = layui.table,
|
||||
laydate = layui.laydate;
|
||||
laydate = layui.laydate,
|
||||
element = layui.element;
|
||||
|
||||
var echartsAttitude = echarts.init(document.getElementById('echarts-attitude'), 'walden');
|
||||
var chartDataLoaded = false;
|
||||
|
||||
var cfg_cols = [
|
||||
{field: 'deviceid', title: '设备号', width: 100},
|
||||
@ -78,9 +97,7 @@
|
||||
{field: 'temperature', title: '温度(°C)'},
|
||||
{field: 'humidity', title: '湿度(%)'}
|
||||
];
|
||||
/**
|
||||
* 初始化表单,要加上,不然刷新部分组件可能会不加载
|
||||
*/
|
||||
|
||||
form.render();
|
||||
|
||||
laydate.render({
|
||||
@ -95,7 +112,7 @@
|
||||
table.render({
|
||||
elem: '#currentTableId',
|
||||
url: '/gnss/msg/status/list',
|
||||
toolbar: '#toolbarDemo',//开启头部工具栏
|
||||
toolbar: '#toolbarDemo',
|
||||
defaultToolbar: ['filter'],
|
||||
cols: [
|
||||
cfg_cols
|
||||
@ -106,11 +123,9 @@
|
||||
skin: 'line'
|
||||
});
|
||||
|
||||
// 监听搜索操作
|
||||
form.on('submit(data-search-btn)', function (data) {
|
||||
var result = JSON.stringify(data.field);
|
||||
|
||||
//执行搜索重载
|
||||
table.reload('currentTableId', {
|
||||
page: {
|
||||
curr: 1
|
||||
@ -123,7 +138,6 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
// 监听导出操作
|
||||
form.on('submit(data-export-btn)', function (data) {
|
||||
var result = $('#searchFrm').serialize();
|
||||
var u = "/gnss/msg/status/export?" + result;
|
||||
@ -131,6 +145,173 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
element.on('tab(data-tab)', function(data){
|
||||
if (data.index == 1) {
|
||||
echartsAttitude.resize();
|
||||
|
||||
if (!chartDataLoaded) {
|
||||
var deviceId = $('#deviceid').val();
|
||||
if (deviceId) {
|
||||
loadChartData(deviceId);
|
||||
} else {
|
||||
layer.msg('请先在搜索框中输入设备号');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function loadChartData(deviceId) {
|
||||
$.ajax({
|
||||
url: '/gnss/msg/status/device_data',
|
||||
type: 'GET',
|
||||
data: {
|
||||
deviceId: deviceId
|
||||
},
|
||||
success: function(res) {
|
||||
if (res.code === 0 && res.data) {
|
||||
showChart(res.data, res.originalData);
|
||||
chartDataLoaded = true;
|
||||
} else {
|
||||
layer.msg('获取数据失败');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
layer.msg('获取数据失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showChart(smoothedData, originalData) {
|
||||
var t = [];
|
||||
var roll = [];
|
||||
var pitch = [];
|
||||
var yaw = [];
|
||||
var smoothRoll = [];
|
||||
var smoothPitch = [];
|
||||
var smoothYaw = [];
|
||||
|
||||
for (var i = 0; i < originalData.length; i++) {
|
||||
t.push(originalData[i].createtime);
|
||||
roll.push(originalData[i].roll);
|
||||
pitch.push(originalData[i].pitch);
|
||||
yaw.push(originalData[i].yaw);
|
||||
}
|
||||
|
||||
for (var i = 0; i < smoothedData.length; i++) {
|
||||
smoothRoll.push(smoothedData[i].roll);
|
||||
smoothPitch.push(smoothedData[i].pitch);
|
||||
smoothYaw.push(smoothedData[i].yaw);
|
||||
}
|
||||
|
||||
var option = {
|
||||
title: {
|
||||
text: '姿态数据曲线'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['Roll', 'Pitch', 'Yaw', '平滑Roll', '平滑Pitch', '平滑Yaw']
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: t,
|
||||
axisLabel: {
|
||||
formatter: function(value) {
|
||||
var date = new Date(value);
|
||||
return date.getFullYear() + '-' +
|
||||
(date.getMonth() + 1).toString().padStart(2, '0') + '-' +
|
||||
date.getDate().toString().padStart(2, '0') + ' ' +
|
||||
date.getHours().toString().padStart(2, '0') + ':' +
|
||||
date.getMinutes().toString().padStart(2, '0') + ':' +
|
||||
date.getSeconds().toString().padStart(2, '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Roll',
|
||||
type: 'line',
|
||||
data: roll,
|
||||
lineStyle: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Pitch',
|
||||
type: 'line',
|
||||
data: pitch,
|
||||
lineStyle: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Yaw',
|
||||
type: 'line',
|
||||
data: yaw,
|
||||
lineStyle: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '平滑Roll',
|
||||
type: 'line',
|
||||
data: smoothRoll,
|
||||
lineStyle: {
|
||||
width: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '平滑Pitch',
|
||||
type: 'line',
|
||||
data: smoothPitch,
|
||||
lineStyle: {
|
||||
width: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '平滑Yaw',
|
||||
type: 'line',
|
||||
data: smoothYaw,
|
||||
lineStyle: {
|
||||
width: 2
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
echartsAttitude.setOption(option);
|
||||
|
||||
window.onresize = function() {
|
||||
echartsAttitude.resize();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user