feat: 新增 485 的解析
This commit is contained in:
parent
8cbde597fd
commit
a6fa18f5cc
17
main.go
17
main.go
@ -121,7 +121,6 @@ func startUDP() {
|
||||
continue
|
||||
}
|
||||
rawData := buffer[:n]
|
||||
data := string(rawData)
|
||||
log.Printf("从 %s 接收到 %d 字节数据", addr.String(), n)
|
||||
|
||||
hexDump := hexDump(rawData)
|
||||
@ -129,13 +128,14 @@ func startUDP() {
|
||||
asciiDump := asciiDump(rawData)
|
||||
log.Printf("ASCII码:\n%s", asciiDump)
|
||||
|
||||
weatherData, err := model.ParseWeatherData(data)
|
||||
weatherData, err := model.ParseData(rawData)
|
||||
if err != nil {
|
||||
log.Printf("解析数据失败: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("成功解析气象站数据:")
|
||||
log.Printf("设备类型: %s", getDeviceTypeString(weatherData.DeviceType))
|
||||
log.Println(weatherData)
|
||||
|
||||
if weatherData.StationID != "" {
|
||||
@ -145,7 +145,7 @@ func startUDP() {
|
||||
log.Printf("警告: 收到的数据没有站点ID")
|
||||
}
|
||||
|
||||
err = model.SaveWeatherData(weatherData, data)
|
||||
err = model.SaveWeatherData(weatherData, string(rawData))
|
||||
if err != nil {
|
||||
log.Printf("保存数据到数据库失败: %v", err)
|
||||
} else {
|
||||
@ -154,6 +154,17 @@ func startUDP() {
|
||||
}
|
||||
}
|
||||
|
||||
func getDeviceTypeString(deviceType model.DeviceType) string {
|
||||
switch deviceType {
|
||||
case model.DeviceTypeWIFI:
|
||||
return "WIFI"
|
||||
case model.DeviceTypeRS485:
|
||||
return "RS485"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
setupLogger()
|
||||
startUDP()
|
||||
|
||||
69
model/db.go
69
model/db.go
@ -30,9 +30,35 @@ func InitDB() error {
|
||||
return fmt.Errorf("数据库连接测试失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建RS485数据表
|
||||
err = createRS485Table()
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建RS485数据表失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createRS485Table() error {
|
||||
_, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS rs485_weather_data (
|
||||
id SERIAL PRIMARY KEY,
|
||||
station_id VARCHAR(50) NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL,
|
||||
temperature DECIMAL(5,2), -- 温度(摄氏度)
|
||||
humidity DECIMAL(5,2), -- 湿度(%)
|
||||
wind_speed DECIMAL(5,2), -- 风速(m/s)
|
||||
wind_direction DECIMAL(5,2), -- 风向(度)
|
||||
rainfall DECIMAL(5,2), -- 降雨量(mm)
|
||||
light DECIMAL(10,2), -- 光照(lux)
|
||||
uv DECIMAL(5,2), -- 紫外线
|
||||
pressure DECIMAL(7,2), -- 气压(hPa)
|
||||
raw_data TEXT, -- 原始数据
|
||||
FOREIGN KEY (station_id) REFERENCES stations(station_id)
|
||||
)`)
|
||||
return err
|
||||
}
|
||||
|
||||
func CloseDB() {
|
||||
if db != nil {
|
||||
db.Close()
|
||||
@ -79,7 +105,19 @@ func SaveWeatherData(data *WeatherData, rawData string) error {
|
||||
cst := time.FixedZone("CST", 8*60*60)
|
||||
timestamp := time.Now().In(cst)
|
||||
|
||||
_, err = db.Exec(`
|
||||
// 根据设备类型选择不同的保存方法
|
||||
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(`
|
||||
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,
|
||||
@ -96,8 +134,33 @@ func SaveWeatherData(data *WeatherData, rawData string) error {
|
||||
int(data.AbsBarometerIn*1000), int(data.BarometerIn*1000), data.LowBattery, rawData)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存气象数据失败: %v", err)
|
||||
return fmt.Errorf("保存WIFI气象数据失败: %v", err)
|
||||
}
|
||||
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
|
||||
|
||||
_, 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)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存RS485气象数据失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -732,3 +732,83 @@ func (p *Protocol) GetPressure() (*Pressure, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// RS485Protocol 定义RS485协议结构
|
||||
type RS485Protocol struct {
|
||||
RawData []byte
|
||||
}
|
||||
|
||||
// NewRS485Protocol 创建新的RS485协议实例
|
||||
func NewRS485Protocol(data []byte) *RS485Protocol {
|
||||
return &RS485Protocol{
|
||||
RawData: data,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateRS485Data 验证RS485数据是否有效
|
||||
func ValidateRS485Data(data []byte) bool {
|
||||
// 检查数据长度是否为25字节
|
||||
if len(data) != 25 {
|
||||
return false
|
||||
}
|
||||
// 检查起始字节是否为0x24
|
||||
if data[0] != 0x24 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RS485WeatherData 存储RS485气象数据
|
||||
type RS485WeatherData struct {
|
||||
Temperature float64 // 温度
|
||||
Humidity float64 // 湿度
|
||||
WindSpeed float64 // 风速
|
||||
WindDirection float64 // 风向
|
||||
Rainfall float64 // 雨量
|
||||
Light float64 // 光照
|
||||
UV float64 // 紫外线
|
||||
Pressure float64 // 气压
|
||||
}
|
||||
|
||||
// ParseRS485Data 解析RS485数据
|
||||
func (p *RS485Protocol) ParseRS485Data() (*RS485WeatherData, error) {
|
||||
if !ValidateRS485Data(p.RawData) {
|
||||
return nil, fmt.Errorf("无效的RS485数据格式")
|
||||
}
|
||||
|
||||
data := &RS485WeatherData{}
|
||||
|
||||
// 解析温度 (索引5-6)
|
||||
tempRaw := int16(p.RawData[5])<<8 | int16(p.RawData[6])
|
||||
data.Temperature = float64(tempRaw) / 10.0
|
||||
|
||||
// 解析湿度 (索引7-8)
|
||||
humRaw := int16(p.RawData[7])<<8 | int16(p.RawData[8])
|
||||
data.Humidity = float64(humRaw) / 10.0
|
||||
|
||||
// 解析风速 (索引9-10)
|
||||
windSpeedRaw := int16(p.RawData[9])<<8 | int16(p.RawData[10])
|
||||
data.WindSpeed = float64(windSpeedRaw) / 10.0
|
||||
|
||||
// 解析风向 (索引11-12)
|
||||
windDirRaw := int16(p.RawData[11])<<8 | int16(p.RawData[12])
|
||||
data.WindDirection = float64(windDirRaw)
|
||||
|
||||
// 解析雨量 (索引13-14)
|
||||
rainRaw := int16(p.RawData[13])<<8 | int16(p.RawData[14])
|
||||
data.Rainfall = float64(rainRaw) / 10.0
|
||||
|
||||
// 解析光照 (索引15-16)
|
||||
lightRaw := int16(p.RawData[15])<<8 | int16(p.RawData[16])
|
||||
data.Light = float64(lightRaw)
|
||||
|
||||
// 解析紫外线 (索引17-18)
|
||||
uvRaw := int16(p.RawData[17])<<8 | int16(p.RawData[18])
|
||||
data.UV = float64(uvRaw) / 10.0
|
||||
|
||||
// 解析气压 (索引19-20)
|
||||
pressureRaw := int16(p.RawData[19])<<8 | int16(p.RawData[20])
|
||||
data.Pressure = float64(pressureRaw) / 10.0
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@ -5,9 +5,19 @@ import (
|
||||
"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
|
||||
@ -39,24 +49,37 @@ type WeatherData struct {
|
||||
|
||||
var urlRegex = regexp.MustCompile(`/weatherstation/updateweatherstation\.php\?([^&\s]+(&[^&\s]+)*)`)
|
||||
|
||||
func ParseWeatherData(data string) (*WeatherData, error) {
|
||||
// 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{}
|
||||
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.DateUTC = values.Get("dateutc")
|
||||
wd.SoftwareType = values.Get("softwaretype")
|
||||
wd.Action = values.Get("action")
|
||||
|
||||
@ -151,6 +174,48 @@ func ParseWeatherData(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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user