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 } }