432 lines
12 KiB
JavaScript
432 lines
12 KiB
JavaScript
let sensorChart = null;
|
||
let refreshInterval = null;
|
||
let allSensors = [];
|
||
let currentSensorData = [];
|
||
|
||
// 页面加载完成后执行
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 初始化日期选择器为今天
|
||
initializeDatePickers();
|
||
|
||
// 加载所有传感器ID
|
||
loadSensors();
|
||
|
||
// 添加事件监听器
|
||
setupEventListeners();
|
||
|
||
// 设置自动刷新
|
||
setupAutoRefresh();
|
||
});
|
||
|
||
// 初始化日期选择器
|
||
function initializeDatePickers() {
|
||
const now = new Date();
|
||
const today = now.toISOString().split('T')[0];
|
||
const time = now.toTimeString().split(' ')[0].substring(0, 5);
|
||
|
||
// 设置默认的开始时间为当天00:00
|
||
document.getElementById('startDate').value = `${today}T00:00`;
|
||
|
||
// 设置默认的结束时间为当前时间
|
||
document.getElementById('endDate').value = `${today}T${time}`;
|
||
}
|
||
|
||
// 设置事件监听器
|
||
function setupEventListeners() {
|
||
// 查询按钮
|
||
document.getElementById('queryBtn').addEventListener('click', function() {
|
||
loadData();
|
||
});
|
||
|
||
// 重置按钮
|
||
document.getElementById('resetBtn').addEventListener('click', function() {
|
||
resetFilters();
|
||
});
|
||
|
||
// 传感器选择变化
|
||
document.getElementById('sensorSelect').addEventListener('change', function() {
|
||
loadData();
|
||
});
|
||
|
||
// 记录数限制变化
|
||
document.getElementById('limitSelect').addEventListener('change', function() {
|
||
loadData();
|
||
});
|
||
|
||
// 导出CSV按钮
|
||
document.getElementById('exportBtn').addEventListener('click', function() {
|
||
exportToCSV();
|
||
});
|
||
}
|
||
|
||
// 设置自动刷新
|
||
function setupAutoRefresh() {
|
||
const autoRefreshCheckbox = document.getElementById('autoRefresh');
|
||
|
||
// 初始化自动刷新
|
||
if (autoRefreshCheckbox.checked) {
|
||
startAutoRefresh();
|
||
}
|
||
|
||
// 监听复选框变化
|
||
autoRefreshCheckbox.addEventListener('change', function() {
|
||
if (this.checked) {
|
||
startAutoRefresh();
|
||
} else {
|
||
stopAutoRefresh();
|
||
}
|
||
});
|
||
}
|
||
|
||
// 开始自动刷新
|
||
function startAutoRefresh() {
|
||
if (refreshInterval) {
|
||
clearInterval(refreshInterval);
|
||
}
|
||
|
||
refreshInterval = setInterval(loadData, 10000); // 10秒刷新一次
|
||
}
|
||
|
||
// 停止自动刷新
|
||
function stopAutoRefresh() {
|
||
if (refreshInterval) {
|
||
clearInterval(refreshInterval);
|
||
refreshInterval = null;
|
||
}
|
||
}
|
||
|
||
// 重置筛选条件
|
||
function resetFilters() {
|
||
initializeDatePickers();
|
||
document.getElementById('sensorSelect').value = 'all';
|
||
document.getElementById('limitSelect').value = '100';
|
||
loadData();
|
||
}
|
||
|
||
// 加载所有传感器ID
|
||
function loadSensors() {
|
||
fetch('/api/sensors')
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error('获取传感器列表失败');
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
allSensors = data;
|
||
updateSensorSelect(data);
|
||
|
||
// 加载数据
|
||
loadData();
|
||
})
|
||
.catch(error => {
|
||
console.error('加载传感器列表出错:', error);
|
||
alert('加载传感器列表出错: ' + error.message);
|
||
});
|
||
}
|
||
|
||
// 更新传感器选择下拉框
|
||
function updateSensorSelect(sensors) {
|
||
const select = document.getElementById('sensorSelect');
|
||
|
||
// 保留"所有传感器"选项
|
||
const allOption = select.querySelector('option[value="all"]');
|
||
select.innerHTML = '';
|
||
select.appendChild(allOption);
|
||
|
||
if (sensors.length === 0) {
|
||
const option = document.createElement('option');
|
||
option.value = '';
|
||
option.textContent = '没有可用的传感器';
|
||
select.appendChild(option);
|
||
return;
|
||
}
|
||
|
||
sensors.forEach(id => {
|
||
const option = document.createElement('option');
|
||
option.value = id;
|
||
option.textContent = `传感器 ${id}`;
|
||
select.appendChild(option);
|
||
});
|
||
}
|
||
|
||
// 加载传感器数据
|
||
function loadData() {
|
||
const sensorID = document.getElementById('sensorSelect').value;
|
||
const limit = document.getElementById('limitSelect').value;
|
||
const startDate = document.getElementById('startDate').value;
|
||
const endDate = document.getElementById('endDate').value;
|
||
|
||
let url = '/api/data?';
|
||
let params = [];
|
||
|
||
// 添加查询参数
|
||
if (sensorID !== 'all') {
|
||
params.push(`sensor_id=${sensorID}`);
|
||
}
|
||
|
||
if (limit) {
|
||
params.push(`limit=${limit}`);
|
||
}
|
||
|
||
if (startDate) {
|
||
params.push(`start_date=${encodeURIComponent(startDate)}`);
|
||
}
|
||
|
||
if (endDate) {
|
||
params.push(`end_date=${encodeURIComponent(endDate)}`);
|
||
}
|
||
|
||
url += params.join('&');
|
||
|
||
// 显示加载状态
|
||
document.getElementById('queryBtn').textContent = '加载中...';
|
||
|
||
fetch(url)
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error('获取传感器数据失败');
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
currentSensorData = data;
|
||
updateTable(data);
|
||
updateChart(data);
|
||
document.getElementById('queryBtn').textContent = '查询数据';
|
||
})
|
||
.catch(error => {
|
||
console.error('加载数据出错:', error);
|
||
alert('加载数据出错: ' + error.message);
|
||
document.getElementById('queryBtn').textContent = '查询数据';
|
||
});
|
||
}
|
||
|
||
// 更新数据表格
|
||
function updateTable(data) {
|
||
const tableBody = document.getElementById('tableBody');
|
||
tableBody.innerHTML = '';
|
||
|
||
if (data.length === 0) {
|
||
const row = document.createElement('tr');
|
||
row.innerHTML = '<td colspan="6" style="text-align: center;">没有数据</td>';
|
||
tableBody.appendChild(row);
|
||
return;
|
||
}
|
||
|
||
data.forEach(item => {
|
||
const row = document.createElement('tr');
|
||
|
||
// 解析时间并调整为中国时间(UTC+8)
|
||
const date = new Date(item.timestamp);
|
||
// 减去8小时,因为数据库时间似乎比实际时间早了8小时
|
||
date.setHours(date.getHours() - 8);
|
||
|
||
// 格式化为中文日期时间格式
|
||
const formattedDate =
|
||
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');
|
||
|
||
row.innerHTML =
|
||
'<td>' + item.id + '</td>' +
|
||
'<td>' + item.sensor_id + '</td>' +
|
||
'<td>' + item.x.toFixed(3) + '</td>' +
|
||
'<td>' + item.y.toFixed(3) + '</td>' +
|
||
'<td>' + item.z.toFixed(3) + '</td>' +
|
||
'<td>' + formattedDate + '</td>';
|
||
|
||
tableBody.appendChild(row);
|
||
});
|
||
}
|
||
|
||
// 更新图表
|
||
function updateChart(data) {
|
||
// 准备图表数据
|
||
const chartData = prepareChartData(data);
|
||
|
||
// 如果图表已经存在,销毁它
|
||
if (sensorChart) {
|
||
sensorChart.destroy();
|
||
}
|
||
|
||
// 获取图表Canvas
|
||
const ctx = document.getElementById('sensorChart').getContext('2d');
|
||
|
||
// 创建新图表
|
||
sensorChart = new Chart(ctx, {
|
||
type: 'line',
|
||
data: chartData,
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: '传感器数据趋势'
|
||
},
|
||
tooltip: {
|
||
callbacks: {
|
||
label: function(context) {
|
||
let label = context.dataset.label || '';
|
||
if (label) {
|
||
label += ': ';
|
||
}
|
||
if (context.parsed.y !== null) {
|
||
label += context.parsed.y.toFixed(3);
|
||
}
|
||
return label;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
scales: {
|
||
x: {
|
||
title: {
|
||
display: true,
|
||
text: '时间'
|
||
}
|
||
},
|
||
y: {
|
||
title: {
|
||
display: true,
|
||
text: '值'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 准备图表数据
|
||
function prepareChartData(data) {
|
||
// 如果没有数据,返回空数据集
|
||
if (data.length === 0) {
|
||
return {
|
||
labels: [],
|
||
datasets: []
|
||
};
|
||
}
|
||
|
||
// 反转数据以便按时间先后顺序显示
|
||
const sortedData = [...data].sort((a, b) => {
|
||
return new Date(a.timestamp) - new Date(b.timestamp);
|
||
});
|
||
|
||
// 获取所有传感器ID
|
||
let sensorIDs = [...new Set(sortedData.map(item => item.sensor_id))];
|
||
|
||
// 按传感器ID分组数据
|
||
let datasets = [];
|
||
let labels = [];
|
||
|
||
// 准备时间标签(使用第一个传感器的数据)
|
||
if (sensorIDs.length > 0) {
|
||
const firstSensorData = sortedData.filter(item => item.sensor_id === sensorIDs[0]);
|
||
labels = firstSensorData.map(item => {
|
||
const date = new Date(item.timestamp);
|
||
date.setHours(date.getHours() - 8); // 调整时区
|
||
return date.toLocaleString('zh-CN', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
});
|
||
}
|
||
|
||
// 定义颜色
|
||
const colors = [
|
||
'rgb(75, 192, 192)',
|
||
'rgb(255, 99, 132)',
|
||
'rgb(54, 162, 235)',
|
||
'rgb(255, 205, 86)',
|
||
'rgb(153, 102, 255)',
|
||
'rgb(255, 159, 64)'
|
||
];
|
||
|
||
// 为X, Y, Z创建不同的数据集
|
||
const dataTypes = [
|
||
{ key: 'x', label: 'X值' },
|
||
{ key: 'y', label: 'Y值' },
|
||
{ key: 'z', label: 'Z值' }
|
||
];
|
||
|
||
sensorIDs.forEach((sensorID, sensorIndex) => {
|
||
const sensorData = sortedData.filter(item => item.sensor_id === sensorID);
|
||
|
||
dataTypes.forEach((type, typeIndex) => {
|
||
const colorIndex = (sensorIndex * dataTypes.length + typeIndex) % colors.length;
|
||
|
||
datasets.push({
|
||
label: `传感器${sensorID} - ${type.label}`,
|
||
data: sensorData.map(item => item[type.key]),
|
||
fill: false,
|
||
borderColor: colors[colorIndex],
|
||
tension: 0.1
|
||
});
|
||
});
|
||
});
|
||
|
||
return {
|
||
labels: labels,
|
||
datasets: datasets
|
||
};
|
||
}
|
||
|
||
// 导出到CSV文件
|
||
function exportToCSV() {
|
||
if (currentSensorData.length === 0) {
|
||
alert('没有数据可导出');
|
||
return;
|
||
}
|
||
|
||
// 准备CSV内容
|
||
let csvContent = "ID,传感器ID,X值,Y值,Z值,时间戳\n";
|
||
|
||
currentSensorData.forEach(item => {
|
||
// 解析时间并调整为中国时间
|
||
const date = new Date(item.timestamp);
|
||
date.setHours(date.getHours() - 8);
|
||
|
||
// 格式化日期
|
||
const formattedDate =
|
||
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');
|
||
|
||
// 添加一行数据
|
||
csvContent +=
|
||
item.id + "," +
|
||
item.sensor_id + "," +
|
||
item.x.toFixed(3) + "," +
|
||
item.y.toFixed(3) + "," +
|
||
item.z.toFixed(3) + "," +
|
||
formattedDate + "\n";
|
||
});
|
||
|
||
// 创建Blob对象
|
||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||
|
||
// 创建下载链接
|
||
const link = document.createElement("a");
|
||
const url = URL.createObjectURL(blob);
|
||
|
||
// 设置下载属性
|
||
link.setAttribute("href", url);
|
||
link.setAttribute("download", "sensor_data.csv");
|
||
|
||
// 添加到文档并点击
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
|
||
// 清理
|
||
document.body.removeChild(link);
|
||
}
|