269 lines
6.8 KiB
Go
269 lines
6.8 KiB
Go
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,
|
||
)
|
||
}
|