207 lines
5.3 KiB
Go
207 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"html/template"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
func startWebServer() {
|
|
if err := os.MkdirAll("templates", 0755); err != nil {
|
|
logger.Fatalf("创建模板目录失败: %v", err)
|
|
}
|
|
|
|
indexPath := filepath.Join("templates", "index.html")
|
|
if _, err := os.Stat(indexPath); os.IsNotExist(err) {
|
|
logger.Printf("警告: %s 不存在", indexPath)
|
|
}
|
|
http.HandleFunc("/", handleIndex)
|
|
http.HandleFunc("/refresh-data", handleRefresh)
|
|
|
|
logger.Printf("启动Web服务器: http://0.0.0.0:10003")
|
|
if err := http.ListenAndServe(":10003", nil); err != nil {
|
|
logger.Fatalf("启动Web服务器失败: %v", err)
|
|
}
|
|
}
|
|
|
|
func handleRefresh(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
http.Error(w, "只支持POST请求", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
activeConnMutex.Lock()
|
|
sensor := activeSensor
|
|
activeConnMutex.Unlock()
|
|
|
|
if sensor == nil {
|
|
http.Error(w, "无活动的传感器连接", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
|
|
if time.Since(sensor.lastQueryTime) < 3*time.Second {
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
if sensor.sendQuery() {
|
|
logger.Printf("用户触发查询命令")
|
|
w.WriteHeader(http.StatusOK)
|
|
} else {
|
|
http.Error(w, "发送查询命令失败", http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
|
page := 1
|
|
pageSize := 20
|
|
|
|
if pageParam := r.URL.Query().Get("page"); pageParam != "" {
|
|
if p, err := strconv.Atoi(pageParam); err == nil && p > 0 {
|
|
page = p
|
|
}
|
|
}
|
|
|
|
offset := (page - 1) * pageSize
|
|
|
|
var total int
|
|
err := db.QueryRow("SELECT COUNT(*) FROM sensor_data").Scan(&total)
|
|
if err != nil {
|
|
http.Error(w, "数据库错误", http.StatusInternalServerError)
|
|
logger.Printf("统计记录错误: %v", err)
|
|
return
|
|
}
|
|
|
|
totalPages := (total + pageSize - 1) / pageSize
|
|
|
|
pages := []int{}
|
|
startPage := page - 2
|
|
if startPage < 1 {
|
|
startPage = 1
|
|
}
|
|
endPage := startPage + 4
|
|
if endPage > totalPages {
|
|
endPage = totalPages
|
|
startPage = endPage - 4
|
|
if startPage < 1 {
|
|
startPage = 1
|
|
}
|
|
}
|
|
for i := startPage; i <= endPage; i++ {
|
|
pages = append(pages, i)
|
|
}
|
|
|
|
rows, err := db.Query(`
|
|
SELECT id, timestamp, wind_speed, wind_force, wind_direction8, wind_direction360,
|
|
humidity, temperature, noise, pm25, pm10, atm_pressure,
|
|
lux20wh, lux20wl, light20w, optical_rain, compass_angle, solar_radiation
|
|
FROM sensor_data
|
|
ORDER BY timestamp DESC
|
|
LIMIT ? OFFSET ?
|
|
`, pageSize, offset)
|
|
|
|
if err != nil {
|
|
http.Error(w, "数据库错误", http.StatusInternalServerError)
|
|
logger.Printf("查询记录错误: %v", err)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var records []SensorData
|
|
for rows.Next() {
|
|
var id int
|
|
var data SensorData
|
|
err := rows.Scan(
|
|
&id, &data.Timestamp, &data.WindSpeed, &data.WindForce, &data.WindDirection8, &data.WindDirection360,
|
|
&data.Humidity, &data.Temperature, &data.Noise, &data.PM25, &data.PM10, &data.AtmPressure,
|
|
&data.Lux20WH, &data.Lux20WL, &data.Light20W, &data.OpticalRain, &data.CompassAngle, &data.SolarRadiation,
|
|
)
|
|
if err != nil {
|
|
http.Error(w, "数据库错误", http.StatusInternalServerError)
|
|
logger.Printf("扫描记录错误: %v", err)
|
|
return
|
|
}
|
|
records = append(records, data)
|
|
}
|
|
|
|
var latest SensorData
|
|
var id int
|
|
err = db.QueryRow(`
|
|
SELECT id, timestamp, wind_speed, wind_force, wind_direction8, wind_direction360,
|
|
humidity, temperature, noise, pm25, pm10, atm_pressure,
|
|
lux20wh, lux20wl, light20w, optical_rain, compass_angle, solar_radiation
|
|
FROM sensor_data
|
|
ORDER BY timestamp DESC
|
|
LIMIT 1
|
|
`).Scan(
|
|
&id, &latest.Timestamp, &latest.WindSpeed, &latest.WindForce, &latest.WindDirection8, &latest.WindDirection360,
|
|
&latest.Humidity, &latest.Temperature, &latest.Noise, &latest.PM25, &latest.PM10, &latest.AtmPressure,
|
|
&latest.Lux20WH, &latest.Lux20WL, &latest.Light20W, &latest.OpticalRain, &latest.CompassAngle, &latest.SolarRadiation,
|
|
)
|
|
|
|
var latestPtr *SensorData
|
|
if err == nil {
|
|
latestPtr = &latest
|
|
}
|
|
|
|
activeConnMutex.Lock()
|
|
connStatus := "未连接"
|
|
clientAddr := clientAddress
|
|
var lastReset time.Time
|
|
|
|
if activeSensor != nil {
|
|
connStatus = "已连接"
|
|
lastReset = activeSensor.lastResetTime
|
|
}
|
|
activeConnMutex.Unlock()
|
|
|
|
lastResetStr := "无记录"
|
|
if !lastReset.IsZero() {
|
|
lastResetStr = lastReset.Format("2006-01-02 15:04:05")
|
|
}
|
|
data := struct {
|
|
Records []SensorData
|
|
Latest *SensorData
|
|
Page int
|
|
PrevPage int
|
|
NextPage int
|
|
TotalPages int
|
|
Pages []int
|
|
ConnStatus string
|
|
ClientAddr string
|
|
LastReset string
|
|
}{
|
|
Records: records,
|
|
Latest: latestPtr,
|
|
Page: page,
|
|
PrevPage: page - 1,
|
|
NextPage: page + 1,
|
|
TotalPages: totalPages,
|
|
Pages: pages,
|
|
ConnStatus: connStatus,
|
|
ClientAddr: clientAddr,
|
|
LastReset: lastResetStr,
|
|
}
|
|
|
|
funcMap := template.FuncMap{
|
|
"div": func(a, b int) float64 {
|
|
return float64(a) / float64(b)
|
|
},
|
|
}
|
|
|
|
tmpl, err := template.New("index.html").Funcs(funcMap).ParseFiles("templates/index.html")
|
|
if err != nil {
|
|
http.Error(w, "模板错误", http.StatusInternalServerError)
|
|
logger.Printf("解析模板错误: %v", err)
|
|
return
|
|
}
|
|
|
|
if err := tmpl.Execute(w, data); err != nil {
|
|
http.Error(w, "模板错误", http.StatusInternalServerError)
|
|
logger.Printf("执行模板错误: %v", err)
|
|
return
|
|
}
|
|
}
|