178 lines
4.5 KiB
Go
178 lines
4.5 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"weatherstation/core/internal/data"
|
|
)
|
|
|
|
import "github.com/gin-gonic/gin"
|
|
|
|
func handleHealth(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"status": "ok", "ts": time.Now().UTC().Format(time.RFC3339)})
|
|
}
|
|
|
|
func handleSystemStatus(c *gin.Context) {
|
|
online := data.OnlineDevices()
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"online_devices": online,
|
|
"server_time": time.Now().Format("2006-01-02 15:04:05"),
|
|
})
|
|
}
|
|
|
|
func handleStations(c *gin.Context) {
|
|
stations, err := data.Stations()
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("query stations failed: %v", err)})
|
|
return
|
|
}
|
|
for i := range stations {
|
|
if len(stations[i].StationID) > 6 {
|
|
hexID := stations[i].StationID[len(stations[i].StationID)-6:]
|
|
if decimalID, err := strconv.ParseInt(hexID, 16, 64); err == nil {
|
|
stations[i].DecimalID = strconv.FormatInt(decimalID, 10)
|
|
}
|
|
}
|
|
}
|
|
c.JSON(http.StatusOK, stations)
|
|
}
|
|
|
|
func handleData(c *gin.Context) {
|
|
idParam := c.Query("hex_id")
|
|
startTime := c.Query("start_time")
|
|
endTime := c.Query("end_time")
|
|
interval := c.DefaultQuery("interval", "1hour")
|
|
|
|
if idParam == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "missing hex_id"})
|
|
return
|
|
}
|
|
upper := strings.ToUpper(idParam)
|
|
var b strings.Builder
|
|
for i := 0; i < len(upper); i++ {
|
|
ch := upper[i]
|
|
if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') {
|
|
b.WriteByte(ch)
|
|
}
|
|
}
|
|
hex := b.String()
|
|
if hex == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid hex_id"})
|
|
return
|
|
}
|
|
if len(hex) < 6 {
|
|
hex = strings.Repeat("0", 6-len(hex)) + hex
|
|
} else if len(hex) > 6 {
|
|
hex = hex[len(hex)-6:]
|
|
}
|
|
stationID := fmt.Sprintf("RS485-%s", hex)
|
|
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
if loc == nil {
|
|
loc = time.FixedZone("CST", 8*3600)
|
|
}
|
|
start, err := time.ParseInLocation("2006-01-02 15:04:05", startTime, loc)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid start_time"})
|
|
return
|
|
}
|
|
end, err := time.ParseInLocation("2006-01-02 15:04:05", endTime, loc)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid end_time"})
|
|
return
|
|
}
|
|
|
|
var points interface{}
|
|
if interval == "raw" {
|
|
points, err = data.SeriesRaw(stationID, start, end)
|
|
} else {
|
|
points, err = data.SeriesFrom10Min(stationID, start, end, interval)
|
|
}
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("query failed: %v", err)})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, points)
|
|
}
|
|
|
|
func handleForecast(c *gin.Context) {
|
|
stationID := c.Query("station_id")
|
|
from := c.Query("from")
|
|
to := c.Query("to")
|
|
provider := c.Query("provider")
|
|
versionsStr := c.DefaultQuery("versions", "1")
|
|
versions, _ := strconv.Atoi(versionsStr)
|
|
if versions <= 0 {
|
|
versions = 1
|
|
}
|
|
|
|
if stationID == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "missing station_id"})
|
|
return
|
|
}
|
|
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
if loc == nil {
|
|
loc = time.FixedZone("CST", 8*3600)
|
|
}
|
|
|
|
var start, end time.Time
|
|
var err error
|
|
if from == "" || to == "" {
|
|
now := time.Now().In(loc)
|
|
start = now.Truncate(time.Hour).Add(1 * time.Hour)
|
|
end = start.Add(3 * time.Hour)
|
|
} else {
|
|
start, err = time.ParseInLocation("2006-01-02 15:04:05", from, loc)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid from"})
|
|
return
|
|
}
|
|
end, err = time.ParseInLocation("2006-01-02 15:04:05", to, loc)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid to"})
|
|
return
|
|
}
|
|
}
|
|
|
|
points, err := data.Forecast(stationID, start, end, provider, versions)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("query forecast failed: %v", err)})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, points)
|
|
}
|
|
|
|
// handleForecastPerf 过去N天大雨预报表现
|
|
// GET /api/forecast/perf?station_id=RS485-XXXXXX&days=30&provider=imdroid_mix
|
|
func handleForecastPerf(c *gin.Context) {
|
|
stationID := c.Query("station_id")
|
|
if stationID == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "missing station_id"})
|
|
return
|
|
}
|
|
daysStr := c.DefaultQuery("days", "30")
|
|
provider := c.Query("provider")
|
|
days, _ := strconv.Atoi(daysStr)
|
|
if days <= 0 || days > 365 {
|
|
days = 30
|
|
}
|
|
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
if loc == nil {
|
|
loc = time.FixedZone("CST", 8*3600)
|
|
}
|
|
now := time.Now().In(loc)
|
|
since := now.AddDate(0, 0, -days)
|
|
|
|
perf, err := data.HeavyRainPerf(stationID, since, provider)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("query perf failed: %v", err)})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, perf)
|
|
}
|