feat: 新增 485 的解析

This commit is contained in:
yarnom 2025-08-03 11:20:12 +08:00
parent a6fa18f5cc
commit 523f489e11
3 changed files with 108 additions and 89 deletions

26
main.go
View File

@ -128,24 +128,36 @@ func startUDP() {
asciiDump := asciiDump(rawData)
log.Printf("ASCII码:\n%s", asciiDump)
weatherData, err := model.ParseData(rawData)
data, deviceType, err := model.ParseData(rawData)
if err != nil {
log.Printf("解析数据失败: %v", err)
continue
}
log.Println("成功解析气象站数据:")
log.Printf("设备类型: %s", getDeviceTypeString(weatherData.DeviceType))
log.Println(weatherData)
log.Printf("设备类型: %s", getDeviceTypeString(deviceType))
log.Println(data)
if weatherData.StationID != "" {
model.RegisterDevice(weatherData.StationID, addr)
log.Printf("设备 %s 已注册IP: %s", weatherData.StationID, addr.String())
var stationID string
switch v := data.(type) {
case *model.WeatherData:
stationID = v.StationID
case *model.RS485WeatherData:
// 设备ID格式: HSB(21) MSB(22) LSB(1)
hsb := rawData[21] // HSB: bit 168-175 (索引21)
msb := rawData[22] // MSB: bit 176-183 (索引22)
lsb := rawData[1] // LSB: bit 8-15 (索引1)
stationID = fmt.Sprintf("RS485-%02X%02X%02X", hsb, msb, lsb)
}
if stationID != "" {
model.RegisterDevice(stationID, addr)
log.Printf("设备 %s 已注册IP: %s", stationID, addr.String())
} else {
log.Printf("警告: 收到的数据没有站点ID")
}
err = model.SaveWeatherData(weatherData, string(rawData))
err = model.SaveWeatherData(data, string(rawData))
if err != nil {
log.Printf("保存数据到数据库失败: %v", err)
} else {

View File

@ -92,32 +92,28 @@ func ensureStationExists(stationID, password string) error {
return nil
}
func SaveWeatherData(data *WeatherData, rawData string) error {
func SaveWeatherData(data interface{}, rawData string) error {
if db == nil {
return fmt.Errorf("数据库未初始化")
}
switch v := data.(type) {
case *WeatherData:
return saveWIFIWeatherData(v, rawData)
case *RS485WeatherData:
return saveRS485WeatherData(v)
default:
return fmt.Errorf("未知的数据类型")
}
}
func saveWIFIWeatherData(data *WeatherData, rawData string) error {
err := ensureStationExists(data.StationID, data.Password)
if err != nil {
return err
}
cst := time.FixedZone("CST", 8*60*60)
timestamp := time.Now().In(cst)
// 根据设备类型选择不同的保存方法
switch data.DeviceType {
case DeviceTypeWIFI:
return saveWIFIWeatherData(data, rawData, timestamp)
case DeviceTypeRS485:
return saveRS485WeatherData(data, rawData, timestamp)
default:
return fmt.Errorf("未知的设备类型")
}
}
func saveWIFIWeatherData(data *WeatherData, rawData string, timestamp time.Time) error {
_, err := db.Exec(`
_, err = db.Exec(`
INSERT INTO weather_data (
station_id, timestamp, temp_f, humidity, dewpoint_f, windchill_f,
wind_dir, wind_speed_mph, wind_gust_mph, rain_in, daily_rain_in,
@ -125,7 +121,7 @@ func saveWIFIWeatherData(data *WeatherData, rawData string, timestamp time.Time)
solar_radiation, uv, indoor_temp_f, indoor_humidity,
abs_barometer_in, barometer_in, low_battery, raw_data
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)`,
data.StationID, timestamp,
data.StationID, time.Now(),
int(data.TempF*10), data.Humidity, int(data.DewpointF*10), int(data.WindchillF*10),
data.WindDir, int(data.WindSpeedMph*100), int(data.WindGustMph*100),
int(data.RainIn*1000), int(data.DailyRainIn*1000), int(data.WeeklyRainIn*1000),
@ -139,25 +135,21 @@ func saveWIFIWeatherData(data *WeatherData, rawData string, timestamp time.Time)
return nil
}
func saveRS485WeatherData(data *WeatherData, rawData string, timestamp time.Time) error {
// 将华氏度转换回摄氏度
tempC := (data.TempF - 32) * 5 / 9
// 将mph转换回m/s
windSpeedMS := data.WindSpeedMph / 2.23694
// 将inch转换回mm
rainfallMM := data.RainIn * 25.4
// 将inHg转换回hPa
pressureHPa := data.BarometerIn * 33.8639
func saveRS485WeatherData(data *RS485WeatherData) error {
err := ensureStationExists(data.StationID, "")
if err != nil {
return err
}
_, err := db.Exec(`
_, err = db.Exec(`
INSERT INTO rs485_weather_data (
station_id, timestamp, temperature, humidity, wind_speed,
wind_direction, rainfall, light, uv, pressure, raw_data
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
data.StationID, timestamp,
tempC, data.Humidity, windSpeedMS,
data.WindDir, rainfallMM, data.SolarRadiation,
data.UV, pressureHPa, rawData)
data.StationID, data.Timestamp,
data.Temperature, data.Humidity, data.WindSpeed,
data.WindDirection, data.Rainfall, data.Light,
data.UV, data.Pressure, data.RawData)
if err != nil {
return fmt.Errorf("保存RS485气象数据失败: %v", err)

View File

@ -16,8 +16,8 @@ const (
DeviceTypeRS485
)
// WeatherData 存储WIFI设备的气象数据
type WeatherData struct {
DeviceType DeviceType
StationID string
Password string
TempF float64
@ -47,17 +47,52 @@ type WeatherData struct {
RTFreq int
}
// RS485WeatherData 存储RS485设备的气象数据
type RS485WeatherData struct {
StationID string // 站点ID
Timestamp time.Time // 时间戳
Temperature float64 // 温度
Humidity float64 // 湿度
WindSpeed float64 // 风速
WindDirection float64 // 风向
Rainfall float64 // 雨量
Light float64 // 光照
UV float64 // 紫外线
Pressure float64 // 气压
RawData string // 原始数据
}
var urlRegex = regexp.MustCompile(`/weatherstation/updateweatherstation\.php\?([^&\s]+(&[^&\s]+)*)`)
// ParseData 根据数据类型解析气象数据
func ParseData(data []byte) (*WeatherData, error) {
func ParseData(data []byte) (interface{}, DeviceType, error) {
// 检查是否为RS485数据
if len(data) == 25 && data[0] == 0x24 {
return ParseRS485WeatherData(data)
protocol := NewRS485Protocol(data)
rs485Data, err := protocol.ParseRS485Data()
if err != nil {
return nil, DeviceTypeRS485, err
}
weatherData := &RS485WeatherData{
StationID: fmt.Sprintf("RS485-%02X%02X", data[1], data[2]),
Timestamp: time.Now(),
Temperature: rs485Data.Temperature,
Humidity: rs485Data.Humidity,
WindSpeed: rs485Data.WindSpeed,
WindDirection: rs485Data.WindDirection,
Rainfall: rs485Data.Rainfall,
Light: rs485Data.Light,
UV: rs485Data.UV,
Pressure: rs485Data.Pressure,
RawData: fmt.Sprintf("%X", data),
}
return weatherData, DeviceTypeRS485, nil
}
// 尝试解析为WIFI数据
return ParseWIFIWeatherData(string(data))
wifiData, err := ParseWIFIWeatherData(string(data))
return wifiData, DeviceTypeWIFI, err
}
// ParseWIFIWeatherData 解析WIFI设备数据
@ -73,10 +108,7 @@ func ParseWIFIWeatherData(data string) (*WeatherData, error) {
return nil, fmt.Errorf("解析查询参数失败: %v", err)
}
wd := &WeatherData{
DeviceType: DeviceTypeWIFI,
DateUTC: time.Now().UTC().Format("2006-01-02 15:04:05"),
}
wd := &WeatherData{}
wd.StationID = values.Get("ID")
wd.Password = values.Get("PASSWORD")
@ -174,48 +206,6 @@ func ParseWIFIWeatherData(data string) (*WeatherData, error) {
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
@ -266,3 +256,28 @@ func (w *WeatherData) String() string {
w.DateUTC,
)
}
func (w *RS485WeatherData) String() string {
return fmt.Sprintf(`
站点ID: %s
温度: %.1f°C
湿度: %.1f%%
风向: %.1f°
风速: %.2f m/s
降雨量: %.2f mm
光照: %.2f lux
紫外线: %.2f
气压: %.2f hPa
时间: %s`,
w.StationID,
w.Temperature,
w.Humidity,
w.WindDirection,
w.WindSpeed,
w.Rainfall,
w.Light,
w.UV,
w.Pressure,
w.Timestamp.Format("2006-01-02 15:04:05"),
)
}