package model import ( "fmt" "net/url" "regexp" "strconv" "time" ) // DeviceType 定义设备类型 type DeviceType int const ( DeviceTypeWIFI DeviceType = iota DeviceTypeRS485 ) type WeatherData struct { DeviceType DeviceType StationID string Password string TempF float64 Humidity int DewpointF float64 WindchillF float64 WindDir int WindSpeedMph float64 WindGustMph float64 RainIn float64 DailyRainIn float64 WeeklyRainIn float64 MonthlyRainIn float64 YearlyRainIn float64 TotalRainIn float64 SolarRadiation float64 UV int IndoorTempF float64 IndoorHumidity int AbsBarometerIn float64 BarometerIn float64 LowBattery bool SoftwareType string DateUTC string Action string RealTime int RTFreq int } var urlRegex = regexp.MustCompile(`/weatherstation/updateweatherstation\.php\?([^&\s]+(&[^&\s]+)*)`) // ParseData 根据数据类型解析气象数据 func ParseData(data []byte) (*WeatherData, error) { // 检查是否为RS485数据 if len(data) == 25 && data[0] == 0x24 { return ParseRS485WeatherData(data) } // 尝试解析为WIFI数据 return ParseWIFIWeatherData(string(data)) } // ParseWIFIWeatherData 解析WIFI设备数据 func ParseWIFIWeatherData(data string) (*WeatherData, error) { matches := urlRegex.FindStringSubmatch(data) if len(matches) < 2 { return nil, fmt.Errorf("无法找到有效的气象站数据URL") } queryString := matches[1] values, err := url.ParseQuery(queryString) if err != nil { return nil, fmt.Errorf("解析查询参数失败: %v", err) } wd := &WeatherData{ DeviceType: DeviceTypeWIFI, DateUTC: time.Now().UTC().Format("2006-01-02 15:04:05"), } wd.StationID = values.Get("ID") wd.Password = values.Get("PASSWORD") wd.SoftwareType = values.Get("softwaretype") wd.Action = values.Get("action") if tempF, err := strconv.ParseFloat(values.Get("tempf"), 64); err == nil { wd.TempF = tempF } if humidity, err := strconv.Atoi(values.Get("humidity")); err == nil { wd.Humidity = humidity } if dewpointF, err := strconv.ParseFloat(values.Get("dewptf"), 64); err == nil { wd.DewpointF = dewpointF } if windchillF, err := strconv.ParseFloat(values.Get("windchillf"), 64); err == nil { wd.WindchillF = windchillF } if windDir, err := strconv.Atoi(values.Get("winddir")); err == nil { wd.WindDir = windDir } if windSpeedMph, err := strconv.ParseFloat(values.Get("windspeedmph"), 64); err == nil { wd.WindSpeedMph = windSpeedMph } if windGustMph, err := strconv.ParseFloat(values.Get("windgustmph"), 64); err == nil { wd.WindGustMph = windGustMph } if rainIn, err := strconv.ParseFloat(values.Get("rainin"), 64); err == nil { wd.RainIn = rainIn } if dailyRainIn, err := strconv.ParseFloat(values.Get("dailyrainin"), 64); err == nil { wd.DailyRainIn = dailyRainIn } if weeklyRainIn, err := strconv.ParseFloat(values.Get("weeklyrainin"), 64); err == nil { wd.WeeklyRainIn = weeklyRainIn } if monthlyRainIn, err := strconv.ParseFloat(values.Get("monthlyrainin"), 64); err == nil { wd.MonthlyRainIn = monthlyRainIn } if yearlyRainIn, err := strconv.ParseFloat(values.Get("yearlyrainin"), 64); err == nil { wd.YearlyRainIn = yearlyRainIn } if totalRainIn, err := strconv.ParseFloat(values.Get("totalrainin"), 64); err == nil { wd.TotalRainIn = totalRainIn } if solarRadiation, err := strconv.ParseFloat(values.Get("solarradiation"), 64); err == nil { wd.SolarRadiation = solarRadiation } if uv, err := strconv.Atoi(values.Get("UV")); err == nil { wd.UV = uv } if indoorTempF, err := strconv.ParseFloat(values.Get("indoortempf"), 64); err == nil { wd.IndoorTempF = indoorTempF } if indoorHumidity, err := strconv.Atoi(values.Get("indoorhumidity")); err == nil { wd.IndoorHumidity = indoorHumidity } if absBarometerIn, err := strconv.ParseFloat(values.Get("absbaromin"), 64); err == nil { wd.AbsBarometerIn = absBarometerIn } if barometerIn, err := strconv.ParseFloat(values.Get("baromin"), 64); err == nil { wd.BarometerIn = barometerIn } if lowBatt, err := strconv.Atoi(values.Get("lowbatt")); err == nil { wd.LowBattery = lowBatt != 0 } if realTime, err := strconv.Atoi(values.Get("realtime")); err == nil { wd.RealTime = realTime } if rtFreq, err := strconv.Atoi(values.Get("rtfreq")); err == nil { wd.RTFreq = rtFreq } return wd, nil } // ParseRS485WeatherData 解析RS485设备数据 func ParseRS485WeatherData(data []byte) (*WeatherData, error) { protocol := NewRS485Protocol(data) rs485Data, err := protocol.ParseRS485Data() if err != nil { return nil, err } wd := &WeatherData{ DeviceType: DeviceTypeRS485, DateUTC: time.Now().UTC().Format("2006-01-02 15:04:05"), StationID: fmt.Sprintf("RS485-%02X%02X", data[1], data[2]), // 使用前两个字节作为设备ID } // 转换温度(摄氏度到华氏度) wd.TempF = rs485Data.Temperature*9/5 + 32 // 转换湿度(直接使用) wd.Humidity = int(rs485Data.Humidity) // 转换风向(直接使用) wd.WindDir = int(rs485Data.WindDirection) // 转换风速(m/s到mph) wd.WindSpeedMph = rs485Data.WindSpeed * 2.23694 // 转换降雨量(mm到inch) wd.RainIn = rs485Data.Rainfall / 25.4 // 转换光照(直接使用) wd.SolarRadiation = rs485Data.Light // 转换UV(直接使用) wd.UV = int(rs485Data.UV) // 转换气压(hPa到inHg) wd.BarometerIn = rs485Data.Pressure / 33.8639 wd.AbsBarometerIn = wd.BarometerIn return wd, nil } func (w *WeatherData) String() string { return fmt.Sprintf(` 站点ID: %s 温度: %.1f°F (%.1f°C) 湿度: %d%% 露点: %.1f°F (%.1f°C) 风寒指数: %.1f°F (%.1f°C) 风向: %d° 风速: %.2f mph (%.2f km/h) 阵风: %.2f mph (%.2f km/h) 降雨量: %.3f 英寸 (%.2f mm) 日降雨量: %.3f 英寸 (%.2f mm) 周降雨量: %.3f 英寸 (%.2f mm) 月降雨量: %.3f 英寸 (%.2f mm) 年降雨量: %.3f 英寸 (%.2f mm) 总降雨量: %.3f 英寸 (%.2f mm) 太阳辐射: %.2f W/m² 紫外线指数: %d 室内温度: %.1f°F (%.1f°C) 室内湿度: %d%% 绝对气压: %.3f 英寸汞柱 (%.2f hPa) 相对气压: %.3f 英寸汞柱 (%.2f hPa) 低电量: %v 软件类型: %s 日期UTC: %s`, w.StationID, w.TempF, (w.TempF-32)*5/9, w.Humidity, w.DewpointF, (w.DewpointF-32)*5/9, w.WindchillF, (w.WindchillF-32)*5/9, w.WindDir, w.WindSpeedMph, w.WindSpeedMph*1.60934, w.WindGustMph, w.WindGustMph*1.60934, w.RainIn, w.RainIn*25.4, w.DailyRainIn, w.DailyRainIn*25.4, w.WeeklyRainIn, w.WeeklyRainIn*25.4, w.MonthlyRainIn, w.MonthlyRainIn*25.4, w.YearlyRainIn, w.YearlyRainIn*25.4, w.TotalRainIn, w.TotalRainIn*25.4, w.SolarRadiation, w.UV, w.IndoorTempF, (w.IndoorTempF-32)*5/9, w.IndoorHumidity, w.AbsBarometerIn, w.AbsBarometerIn*33.8639, w.BarometerIn, w.BarometerIn*33.8639, w.LowBattery, w.SoftwareType, w.DateUTC, ) }