diff --git a/internal/dao/sensor.go b/internal/dao/sensor.go index c57f6b8..6c28927 100644 --- a/internal/dao/sensor.go +++ b/internal/dao/sensor.go @@ -67,9 +67,9 @@ func (dao *SensorDAO) GetAggregatedData(start, end time.Time, interval string) ( ROUND(AVG(temperature)/10, 1) AS avg_temp, MAX(rainfall) - MIN(rainfall) AS rainfall, ROUND(AVG(humidity)/10, 1) AS avg_humidity, - ROUND(AVG(wind_speed)/10, 1) AS avg_wind_speed, + ROUND(AVG(wind_speed)/100, 1) AS avg_wind_speed, ROUND(AVG(atm_pressure)/10, 1) AS avg_atm_pressure, - ROUND(AVG(solar_radiation)/10, 1) AS avg_solar_radiation + ROUND(AVG(solar_radiation), 1) AS avg_solar_radiation FROM sensor_data WHERE timestamp BETWEEN ? AND ? GROUP BY bucket_time diff --git a/internal/handler/sensor.go b/internal/handler/sensor.go index 4a3dfc4..002b6fb 100644 --- a/internal/handler/sensor.go +++ b/internal/handler/sensor.go @@ -6,6 +6,7 @@ import ( "time" "go_rain_dtu/internal/dao" + "go_rain_dtu/internal/tcp" "go_rain_dtu/pkg/logger" ) @@ -63,3 +64,24 @@ func (h *SensorHandler) ServeStatic(w http.ResponseWriter, r *http.Request) { } http.FileServer(http.Dir("static")).ServeHTTP(w, r) } + +// GetConnectionStatus 返回当前TCP连接状态 +func (h *SensorHandler) GetConnectionStatus(w http.ResponseWriter, r *http.Request) { + connected, ip, port := tcp.GetConnectionStatus() + + status := struct { + Connected bool `json:"connected"` + IP string `json:"ip"` + Port string `json:"port"` + }{ + Connected: connected, + IP: ip, + Port: port, + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(status); err != nil { + logger.Logger.Printf("JSON编码失败: %v", err) + http.Error(w, "服务器内部错误", http.StatusInternalServerError) + } +} diff --git a/internal/tcp/sensor.go b/internal/tcp/sensor.go index 656b742..a8c63b1 100644 --- a/internal/tcp/sensor.go +++ b/internal/tcp/sensor.go @@ -20,6 +20,51 @@ const ( tcpPort = ":10004" // TCP服务器端口 ) +// ConnectionStatus 保存TCP连接状态 +var ( + ConnectionStatus = struct { + Connected bool + IP string + Port string + LastSeen time.Time + mu sync.RWMutex + }{ + Connected: false, + IP: "", + Port: "", + } +) + +// GetConnectionStatus 返回当前连接状态 +func GetConnectionStatus() (bool, string, string) { + ConnectionStatus.mu.RLock() + defer ConnectionStatus.mu.RUnlock() + + // 如果最后一次通信时间超过1分钟,认为连接已断开 + if ConnectionStatus.Connected && time.Since(ConnectionStatus.LastSeen) > time.Minute { + return false, ConnectionStatus.IP, ConnectionStatus.Port + } + + return ConnectionStatus.Connected, ConnectionStatus.IP, ConnectionStatus.Port +} + +// updateConnectionStatus 更新连接状态 +func updateConnectionStatus(connected bool, addr string) { + ConnectionStatus.mu.Lock() + defer ConnectionStatus.mu.Unlock() + + ConnectionStatus.Connected = connected + if connected && addr != "" { + host, port, _ := net.SplitHostPort(addr) + ConnectionStatus.IP = host + ConnectionStatus.Port = port + ConnectionStatus.LastSeen = time.Now() + } else if !connected { + // 如果断开连接,只更新状态,保留最后的IP和端口信息 + ConnectionStatus.Connected = false + } +} + type SensorComm struct { conn net.Conn dao *dao.SensorDAO @@ -62,9 +107,11 @@ func handleConnection(sensor *SensorComm) { defer sensor.Close() logger.Logger.Printf("新连接建立: %s", sensor.address) + updateConnectionStatus(true, sensor.address) // 发送首次查询 if err := sensor.sendQuery(); err != nil { + updateConnectionStatus(false, "") return } @@ -99,11 +146,15 @@ func handleConnection(sensor *SensorComm) { // 其他错误才认为连接断开 logger.Logger.Printf("读取数据失败: %v", err) + updateConnectionStatus(false, "") done <- true return } if n > 0 { + // 更新最后通信时间 + updateConnectionStatus(true, sensor.address) + // 记录原始数据 logger.Logger.Printf("接收到原始数据 [%s]: % X", sensor.address, buffer[:n]) @@ -125,9 +176,11 @@ func handleConnection(sensor *SensorComm) { case <-ticker.C: logger.Logger.Printf("定时查询触发 [%s]", sensor.address) if err := sensor.sendQuery(); err != nil { + updateConnectionStatus(false, "") return } case <-done: + updateConnectionStatus(false, "") return } } @@ -155,18 +208,30 @@ func (s *SensorComm) handleData(data []byte) *model.SensorData { } // 解析数据,从第4个字节开始是数据部分 + // 根据原始TCP服务器代码中的单位转换 + windSpeed := int(binary.BigEndian.Uint16(data[3:5])) // 风速值*100 sensorData := &model.SensorData{ Timestamp: time.Now(), - WindSpeed: int(binary.BigEndian.Uint16(data[3:5])), + WindSpeed: windSpeed, // 原始值已经是*100的 WindForce: int(binary.BigEndian.Uint16(data[5:7])), WindDirection8: int(binary.BigEndian.Uint16(data[7:9])), WindDirection360: int(binary.BigEndian.Uint16(data[9:11])), - Humidity: int(binary.BigEndian.Uint16(data[11:13])), - Temperature: int(binary.BigEndian.Uint16(data[13:15])), - AtmPressure: int(binary.BigEndian.Uint16(data[21:23])), - SolarRadiation: int(binary.BigEndian.Uint16(data[33:35])), + Humidity: int(binary.BigEndian.Uint16(data[11:13])), // 湿度*10 + Temperature: int(binary.BigEndian.Uint16(data[13:15])), // 温度*10 + AtmPressure: int(binary.BigEndian.Uint16(data[21:23])), // 大气压*10 + SolarRadiation: int(binary.BigEndian.Uint16(data[33:35])), // 太阳辐射原始值 } + // 记录解析后的传感器数据,展示实际物理量 + logger.Logger.Printf("传感器数据: 风速=%.2f m/s, 风力=%d级, 风向=%d°, 湿度=%.1f%%, 温度=%.1f℃, 大气压=%.1f kPa, 太阳辐射=%d W/m²", + float64(sensorData.WindSpeed)/100.0, + sensorData.WindForce, + sensorData.WindDirection360, + float64(sensorData.Humidity)/10.0, + float64(sensorData.Temperature)/10.0, + float64(sensorData.AtmPressure)/10.0, + sensorData.SolarRadiation) + // 保存数据到数据库 if err := s.dao.Insert(sensorData); err != nil { logger.Logger.Printf("保存数据失败: %v", err) @@ -184,6 +249,7 @@ func (s *SensorComm) Close() { if s.conn != nil { s.conn.Close() s.conn = nil + updateConnectionStatus(false, "") } } diff --git a/main.go b/main.go index f4ed2cd..44c78c9 100644 --- a/main.go +++ b/main.go @@ -52,6 +52,7 @@ func main() { // 设置路由 http.HandleFunc("/api/data", sensorHandler.GetAggregatedData) + http.HandleFunc("/api/status", sensorHandler.GetConnectionStatus) http.HandleFunc("/", sensorHandler.ServeStatic) // 启动TCP服务器 diff --git a/static/index.html b/static/index.html index c8b590e..f2065ee 100644 --- a/static/index.html +++ b/static/index.html @@ -118,6 +118,9 @@

雨量计数据

+
+ 未连接 +
@@ -141,10 +144,6 @@
-
- - -
@@ -162,10 +161,10 @@ 时间 降雨量(mm) - 平均温度(℃) - 平均湿度(%) - 平均风速(m/s) - 大气压(hPa) + 温度(℃) + 湿度(%) + 风速(m/s) + 大气压(kPa) 太阳辐射(W/m²) @@ -176,6 +175,29 @@