feat: 新增1分钟的粒度
This commit is contained in:
parent
74aaef9ad5
commit
da2e0f6c99
@ -219,3 +219,33 @@ func findNearestPoint(t time.Time, timestamps []int64, dataMap map[int64]model.A
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -128,3 +128,58 @@ func (h *SensorHandler) GetLatestData(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
1
main.go
1
main.go
@ -54,6 +54,7 @@ func main() {
|
||||
http.HandleFunc("/api/data", sensorHandler.GetAggregatedData)
|
||||
http.HandleFunc("/api/latest", sensorHandler.GetLatestData)
|
||||
http.HandleFunc("/api/status", sensorHandler.GetConnectionStatus)
|
||||
http.HandleFunc("/api/raw/latest", sensorHandler.GetLatestSensorRawData)
|
||||
http.HandleFunc("/", sensorHandler.ServeStatic)
|
||||
|
||||
// 启动TCP服务器
|
||||
|
||||
@ -41,6 +41,49 @@
|
||||
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 {
|
||||
padding: 5px 10px;
|
||||
border: 1px solid #ddd;
|
||||
@ -128,7 +171,7 @@
|
||||
<div class="control-group">
|
||||
<label for="interval">数据粒度:</label>
|
||||
<select id="interval">
|
||||
<option value="1min">1分钟</option>
|
||||
<option value="1min">1分钟(测试用)</option>
|
||||
<option value="5min" selected>5分钟</option>
|
||||
<option value="30min">30分钟</option>
|
||||
<option value="1hour">1小时</option>
|
||||
@ -145,7 +188,6 @@
|
||||
<input type="datetime-local" id="endDate">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="control-group">
|
||||
<button onclick="queryData()">查询历史数据</button>
|
||||
<button onclick="queryLatestData()">查询最新数据</button>
|
||||
@ -153,6 +195,48 @@
|
||||
</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">
|
||||
<canvas id="mainChart"></canvas>
|
||||
</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() {
|
||||
const interval = document.getElementById('interval').value;
|
||||
@ -280,6 +389,9 @@
|
||||
// 加载状态指示
|
||||
document.getElementById('mainChart').style.opacity = 0.5;
|
||||
|
||||
// 同时获取最新的传感器原始数据
|
||||
fetchLatestSensorData();
|
||||
|
||||
fetch(`/api/latest?interval=${interval}&start=${startDateTime}&end=${endDateTime}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
@ -480,10 +592,14 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initDatePickers();
|
||||
queryLatestData(); // 初始加载最新数据
|
||||
fetchLatestSensorData(); // 获取最新传感器原始数据
|
||||
|
||||
// 每30秒检查一次连接状态
|
||||
checkConnectionStatus();
|
||||
connectionCheckTimer = setInterval(checkConnectionStatus, 30000);
|
||||
|
||||
// 每分钟自动刷新最新传感器数据
|
||||
setInterval(fetchLatestSensorData, 60000);
|
||||
});
|
||||
|
||||
// 页面卸载时清除定时器
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user