feat: 新增1分钟的粒度

This commit is contained in:
fengyarnom 2025-05-15 18:45:58 +08:00
parent 74aaef9ad5
commit da2e0f6c99
4 changed files with 204 additions and 2 deletions

View File

@ -219,3 +219,33 @@ func findNearestPoint(t time.Time, timestamps []int64, dataMap map[int64]model.A
return dataMap[nearest] return dataMap[nearest]
} }
// GetLatestSensorData 获取最新的一条传感器数据
func (dao *SensorDAO) GetLatestSensorData() (*model.SensorData, error) {
query := `
SELECT id, timestamp, wind_speed, wind_force, wind_direction_8,
wind_direction_360, humidity, temperature, atm_pressure,
solar_radiation, rainfall
FROM sensor_data
ORDER BY timestamp DESC
LIMIT 1
`
var data model.SensorData
err := dao.db.QueryRow(query).Scan(
&data.ID, &data.Timestamp, &data.WindSpeed, &data.WindForce, &data.WindDirection8,
&data.WindDirection360, &data.Humidity, &data.Temperature, &data.AtmPressure,
&data.SolarRadiation, &data.Rainfall,
)
if err != nil {
if err == sql.ErrNoRows {
logger.Logger.Printf("未找到传感器数据")
return nil, nil
}
logger.Logger.Printf("获取最新传感器数据失败: %v", err)
return nil, err
}
return &data, nil
}

View File

@ -128,3 +128,58 @@ func (h *SensorHandler) GetLatestData(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
// GetLatestSensorRawData 获取最新的一条传感器原始数据
func (h *SensorHandler) GetLatestSensorRawData(w http.ResponseWriter, r *http.Request) {
data, err := h.dao.GetLatestSensorData()
if err != nil {
logger.Logger.Printf("获取最新传感器数据失败: %v", err)
http.Error(w, "服务器内部错误", http.StatusInternalServerError)
return
}
if data == nil {
// 没有数据,返回空对象
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("{}"))
return
}
// 转换为前端友好的格式
response := struct {
ID int64 `json:"id"`
Timestamp time.Time `json:"timestamp"`
FormattedTime string `json:"formatted_time"`
WindSpeed float64 `json:"wind_speed"` // 风速(m/s)
WindForce int `json:"wind_force"` // 风力(级)
WindDirection8 int `json:"wind_direction_8"` // 8方位风向
WindDirection360 int `json:"wind_direction_360"` // 360度风向
Humidity float64 `json:"humidity"` // 湿度(%)
Temperature float64 `json:"temperature"` // 温度(℃)
AtmPressure float64 `json:"atm_pressure"` // 大气压(kPa)
SolarRadiation int `json:"solar_radiation"` // 太阳辐射(W/m²)
Rainfall int `json:"rainfall"` // 累计雨量(mm)
}{
ID: data.ID,
Timestamp: data.Timestamp,
FormattedTime: data.Timestamp.Format("2006-01-02 15:04:05"),
WindSpeed: float64(data.WindSpeed) / 100.0,
WindForce: data.WindForce,
WindDirection8: data.WindDirection8,
WindDirection360: data.WindDirection360,
Humidity: float64(data.Humidity) / 10.0,
Temperature: float64(data.Temperature) / 10.0,
AtmPressure: float64(data.AtmPressure) / 10.0,
SolarRadiation: data.SolarRadiation,
Rainfall: data.Rainfall,
}
w.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
if err := encoder.Encode(response); err != nil {
logger.Logger.Printf("JSON编码失败: %v", err)
http.Error(w, "服务器内部错误", http.StatusInternalServerError)
return
}
}

View File

@ -54,6 +54,7 @@ func main() {
http.HandleFunc("/api/data", sensorHandler.GetAggregatedData) http.HandleFunc("/api/data", sensorHandler.GetAggregatedData)
http.HandleFunc("/api/latest", sensorHandler.GetLatestData) http.HandleFunc("/api/latest", sensorHandler.GetLatestData)
http.HandleFunc("/api/status", sensorHandler.GetConnectionStatus) http.HandleFunc("/api/status", sensorHandler.GetConnectionStatus)
http.HandleFunc("/api/raw/latest", sensorHandler.GetLatestSensorRawData)
http.HandleFunc("/", sensorHandler.ServeStatic) http.HandleFunc("/", sensorHandler.ServeStatic)
// 启动TCP服务器 // 启动TCP服务器

View File

@ -41,6 +41,49 @@
gap: 5px; gap: 5px;
} }
.latest-data {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f8f9fa;
}
.latest-data h3 {
margin-top: 0;
margin-bottom: 10px;
color: #333;
}
.data-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
}
.data-item {
padding: 8px;
border: 1px solid #eee;
border-radius: 4px;
background-color: white;
}
.data-label {
font-weight: bold;
color: #555;
font-size: 12px;
}
.data-value {
font-size: 18px;
color: #007bff;
}
.data-unit {
font-size: 12px;
color: #777;
}
select, input, button { select, input, button {
padding: 5px 10px; padding: 5px 10px;
border: 1px solid #ddd; border: 1px solid #ddd;
@ -128,7 +171,7 @@
<div class="control-group"> <div class="control-group">
<label for="interval">数据粒度:</label> <label for="interval">数据粒度:</label>
<select id="interval"> <select id="interval">
<option value="1min">1分钟</option> <option value="1min">1分钟(测试用)</option>
<option value="5min" selected>5分钟</option> <option value="5min" selected>5分钟</option>
<option value="30min">30分钟</option> <option value="30min">30分钟</option>
<option value="1hour">1小时</option> <option value="1hour">1小时</option>
@ -145,7 +188,6 @@
<input type="datetime-local" id="endDate"> <input type="datetime-local" id="endDate">
</div> </div>
<div class="control-group"> <div class="control-group">
<button onclick="queryData()">查询历史数据</button> <button onclick="queryData()">查询历史数据</button>
<button onclick="queryLatestData()">查询最新数据</button> <button onclick="queryLatestData()">查询最新数据</button>
@ -153,6 +195,48 @@
</div> </div>
</div> </div>
<!-- 最新数据显示区域 -->
<div class="latest-data">
<h3>最新传感器数据 <span id="latest-time" style="font-weight: normal; font-size: 14px;"></span></h3>
<div class="data-grid">
<div class="data-item">
<div class="data-label">温度</div>
<div class="data-value" id="latest-temperature">--</div>
<div class="data-unit"></div>
</div>
<div class="data-item">
<div class="data-label">湿度</div>
<div class="data-value" id="latest-humidity">--</div>
<div class="data-unit">%</div>
</div>
<div class="data-item">
<div class="data-label">风速</div>
<div class="data-value" id="latest-wind-speed">--</div>
<div class="data-unit">m/s</div>
</div>
<div class="data-item">
<div class="data-label">风向</div>
<div class="data-value" id="latest-wind-direction">--</div>
<div class="data-unit">°</div>
</div>
<div class="data-item">
<div class="data-label">大气压</div>
<div class="data-value" id="latest-atm-pressure">--</div>
<div class="data-unit">kPa</div>
</div>
<div class="data-item">
<div class="data-label">太阳辐射</div>
<div class="data-value" id="latest-solar-radiation">--</div>
<div class="data-unit">W/m²</div>
</div>
<div class="data-item">
<div class="data-label">累计雨量</div>
<div class="data-value" id="latest-rainfall">--</div>
<div class="data-unit">mm</div>
</div>
</div>
</div>
<div class="chart-container"> <div class="chart-container">
<canvas id="mainChart"></canvas> <canvas id="mainChart"></canvas>
</div> </div>
@ -252,6 +336,31 @@
}); });
} }
// 获取最新传感器数据
function fetchLatestSensorData() {
fetch('/api/raw/latest')
.then(response => response.json())
.then(data => {
if (!data || !data.timestamp) {
console.log('No latest sensor data available');
return;
}
// 更新最新数据显示
document.getElementById('latest-time').textContent = `(${data.formatted_time})`;
document.getElementById('latest-temperature').textContent = data.temperature.toFixed(1);
document.getElementById('latest-humidity').textContent = data.humidity.toFixed(1);
document.getElementById('latest-wind-speed').textContent = data.wind_speed.toFixed(2);
document.getElementById('latest-wind-direction').textContent = data.wind_direction_360;
document.getElementById('latest-atm-pressure').textContent = data.atm_pressure.toFixed(1);
document.getElementById('latest-solar-radiation').textContent = data.solar_radiation;
document.getElementById('latest-rainfall').textContent = data.rainfall;
})
.catch(error => {
console.error('获取最新传感器数据失败:', error);
});
}
// 查询最新数据 // 查询最新数据
function queryLatestData() { function queryLatestData() {
const interval = document.getElementById('interval').value; const interval = document.getElementById('interval').value;
@ -280,6 +389,9 @@
// 加载状态指示 // 加载状态指示
document.getElementById('mainChart').style.opacity = 0.5; document.getElementById('mainChart').style.opacity = 0.5;
// 同时获取最新的传感器原始数据
fetchLatestSensorData();
fetch(`/api/latest?interval=${interval}&start=${startDateTime}&end=${endDateTime}`) fetch(`/api/latest?interval=${interval}&start=${startDateTime}&end=${endDateTime}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
@ -480,10 +592,14 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
initDatePickers(); initDatePickers();
queryLatestData(); // 初始加载最新数据 queryLatestData(); // 初始加载最新数据
fetchLatestSensorData(); // 获取最新传感器原始数据
// 每30秒检查一次连接状态 // 每30秒检查一次连接状态
checkConnectionStatus(); checkConnectionStatus();
connectionCheckTimer = setInterval(checkConnectionStatus, 30000); connectionCheckTimer = setInterval(checkConnectionStatus, 30000);
// 每分钟自动刷新最新传感器数据
setInterval(fetchLatestSensorData, 60000);
}); });
// 页面卸载时清除定时器 // 页面卸载时清除定时器