From 6e643497a12926c52cfdb51250ff5400cbaa6909 Mon Sep 17 00:00:00 2001 From: yarnom Date: Sun, 3 Aug 2025 13:50:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20485=20=E7=9A=84?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 146 ++++++++++++++++++++++++++++++----------- model/protocol_test.go | 131 +++++++++++++++++++++++++++++++++++- 2 files changed, 237 insertions(+), 40 deletions(-) diff --git a/main.go b/main.go index 0865a45..1946d10 100644 --- a/main.go +++ b/main.go @@ -128,45 +128,43 @@ func startUDP() { asciiDump := asciiDump(rawData) log.Printf("ASCII码:\n%s", asciiDump) - data, deviceType, err := model.ParseData(rawData) - if err != nil { - log.Printf("解析数据失败: %v", err) - continue - } + // 检查数据是否为RS485格式 + if len(rawData) == 25 && rawData[0] == 0x24 { + // 解析RS485数据 + hexStr := strings.ReplaceAll(hexDump, "\n", " ") + parseHexData(hexStr) - log.Println("成功解析气象站数据:") - log.Printf("设备类型: %s", getDeviceTypeString(deviceType)) - log.Println(data) - - // 如果是RS485数据,打印更详细的信息 - if deviceType == model.DeviceTypeRS485 { - if rs485Data, ok := data.(*model.RS485WeatherData); ok { - log.Println("RS485数据详情:") - log.Printf("设备ID: %s", rs485Data.DeviceID) - log.Printf("温度: %.2f°C", rs485Data.Temperature) - log.Printf("湿度: %.2f%%", rs485Data.Humidity) - log.Printf("风向: %.2f°", rs485Data.WindDirection) - log.Printf("风速: %.2f m/s", rs485Data.WindSpeed) - log.Printf("降雨量: %.2f mm", rs485Data.Rainfall) - log.Printf("光照: %.2f lux", rs485Data.Light) - log.Printf("紫外线: %.2f", rs485Data.UV) - log.Printf("气压: %.2f hPa", rs485Data.Pressure) + // 注册设备 + protocol := model.NewProtocol(rawData) + idParts, err := protocol.GetCompleteID() + if err == nil { + stationID := fmt.Sprintf("RS485-%s", idParts.Complete.Hex) + model.RegisterDevice(stationID, addr) + log.Printf("设备 %s 已注册,IP: %s", stationID, addr.String()) } - } - - var stationID string - switch v := data.(type) { - case *model.WeatherData: - stationID = v.StationID - case *model.RS485WeatherData: - stationID = fmt.Sprintf("RS485-%s", v.DeviceID) - } - - if stationID != "" { - model.RegisterDevice(stationID, addr) - log.Printf("设备 %s 已注册,IP: %s", stationID, addr.String()) } else { - log.Printf("警告: 收到的数据没有站点ID") + // 尝试解析WIFI数据 + data, deviceType, err := model.ParseData(rawData) + if err != nil { + log.Printf("解析数据失败: %v", err) + continue + } + + log.Println("成功解析气象站数据:") + log.Printf("设备类型: %s", getDeviceTypeString(deviceType)) + log.Println(data) + + if deviceType == model.DeviceTypeWIFI { + if wifiData, ok := data.(*model.WeatherData); ok { + stationID := wifiData.StationID + if stationID != "" { + model.RegisterDevice(stationID, addr) + log.Printf("设备 %s 已注册,IP: %s", stationID, addr.String()) + } else { + log.Printf("警告: 收到的数据没有站点ID") + } + } + } } // 暂时不保存到数据库 @@ -191,8 +189,21 @@ func getDeviceTypeString(deviceType model.DeviceType) string { } func main() { - setupLogger() - startUDP() + // 检查是否有命令行参数 + if len(os.Args) > 1 && os.Args[1] == "parse" { + if len(os.Args) > 2 { + // 解析指定的十六进制数据 + hexData := os.Args[2] + parseHexData(hexData) + } else { + fmt.Println("用法: ./weatherstation parse <十六进制数据>") + fmt.Println("示例: ./weatherstation parse \"24 F2 10 02 C7 48 10 03 00 6A 03 E8 05 F5 96 10 3F 01 83 2D B1 00 29 9B A4\"") + } + } else { + // 正常启动服务器 + setupLogger() + startUDP() + } } func hexDump(data []byte) string { @@ -234,3 +245,62 @@ func asciiDump(data []byte) string { } return result.String() } + +// parseHexData 解析十六进制字符串数据 +func parseHexData(hexStr string) { + // 移除所有空格 + hexStr = strings.ReplaceAll(hexStr, " ", "") + + // 将十六进制字符串转换为字节数组 + data, err := hex.DecodeString(hexStr) + if err != nil { + log.Printf("解析十六进制字符串失败: %v", err) + return + } + + // 检查数据有效性 + if !model.ValidateRS485Data(data) { + log.Printf("无效的RS485数据格式: 长度=%d, 起始字节=%02X", len(data), data[0]) + return + } + + // 创建协议解析器 + protocol := model.NewProtocol(data) + rs485Protocol := model.NewRS485Protocol(data) + + // 获取设备ID + idParts, err := protocol.GetCompleteID() + if err != nil { + log.Printf("获取设备ID失败: %v", err) + return + } + + // 解析RS485数据 + rs485Data, err := rs485Protocol.ParseRS485Data() + if err != nil { + log.Printf("解析RS485数据失败: %v", err) + return + } + + // 添加设备ID和时间戳 + rs485Data.DeviceID = idParts.Complete.Hex + rs485Data.ReceivedAt = time.Now() + rs485Data.RawDataHex = fmt.Sprintf("%X", data) + + // 打印解析结果 + log.Println("=== RS485数据解析结果 ===") + log.Printf("原始数据: %s", hexStr) + log.Printf("设备ID: %s (HSB=%s, MSB=%s, LSB=%s)", + idParts.Complete.Hex, + idParts.HSB.Hex, + idParts.MSB.Hex, + idParts.LSB.Hex) + log.Printf("温度: %.2f°C", rs485Data.Temperature) + log.Printf("湿度: %.1f%%", rs485Data.Humidity) + log.Printf("风向: %.1f°", rs485Data.WindDirection) + log.Printf("风速: %.2f m/s", rs485Data.WindSpeed) + log.Printf("降雨量: %.2f mm", rs485Data.Rainfall) + log.Printf("光照: %.2f lux", rs485Data.Light) + log.Printf("紫外线: %.2f", rs485Data.UV) + log.Printf("气压: %.2f hPa", rs485Data.Pressure) +} diff --git a/model/protocol_test.go b/model/protocol_test.go index 6d3c1eb..8dd7057 100644 --- a/model/protocol_test.go +++ b/model/protocol_test.go @@ -549,8 +549,8 @@ func TestGetPressure(t *testing.T) { func TestParseNewData(t *testing.T) { // 新的测试数据:24 F2 30 02 AF 51 03 01 00 08 00 00 00 00 00 6E C2 01 82 D8 5B 00 29 87 EA - data := []byte{0x24, 0xF2, 0x30, 0x02, 0xAF, 0x51, 0x03, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xC2, 0x01, 0x82, 0xD8, 0x5B, 0x00, 0x29, 0x87, 0xEA} - + //data := []byte{0x24, 0xF2, 0x30, 0x02, 0xAF, 0x51, 0x03, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0xC2, 0x01, 0x82, 0xD8, 0x5B, 0x00, 0x29, 0x87, 0xEA} + data := []byte{0x24, 0xF2, 0x10, 0x02, 0xC7, 0x48, 0x10, 0x03, 0x00, 0x6A, 0x03, 0xE8, 0x05, 0xF5, 0x96, 0x10, 0x3F, 0x01, 0x83, 0x2D, 0xB1, 0x00, 0x29, 0x9B, 0xA4} protocol := NewProtocol(data) // 1. 解析风速 @@ -686,3 +686,130 @@ func TestParseNewDataWithDetails(t *testing.T) { t.Logf("设备ID: %02X %02X %02X", id.HSB.Dec, id.MSB.Dec, id.LSB.Dec) t.Logf("原始字节: HSB=%02X, MSB=%02X, LSB=%02X", data[21], data[22], data[1]) } + +func TestParseSpecificData(t *testing.T) { + // 测试数据:24 F2 10 02 C7 48 10 03 00 6A 03 E8 05 F5 96 10 3F 01 83 2D B1 00 29 9B A4 + data := []byte{0x24, 0xF2, 0x10, 0x02, 0xC7, 0x48, 0x10, 0x03, 0x00, 0x6A, 0x03, 0xE8, 0x05, 0xF5, 0x96, 0x10, 0x3F, 0x01, 0x83, 0x2D, 0xB1, 0x00, 0x29, 0x9B, 0xA4} + + protocol := NewProtocol(data) + + t.Log("\n=== 特定数据解析测试 ===") + + // 1. 设备ID解析 + t.Log("\n=== 设备ID解析 ===") + id, err := protocol.GetCompleteID() + if err != nil { + t.Fatalf("获取设备ID失败: %v", err) + } + t.Logf("设备ID: %s", id.Complete.Hex) + t.Logf("原始字节: HSB=%02X, MSB=%02X, LSB=%02X", data[21], data[22], data[1]) + + // 2. 温度解析 + t.Log("\n=== 温度解析 ===") + temp, err := protocol.GetTemperature() + if err != nil { + t.Fatalf("获取温度失败: %v", err) + } + t.Logf("温度: %.2f °C (raw: 0x%X)", temp.Complete.Value, temp.Complete.RawValue) + t.Logf("TMP_H: %s (0x%X), TMP_M: %s (0x%X), TMP_L: %s (0x%X)", + temp.TmpH.Binary, temp.TmpH.Value, + temp.TmpM.Binary, temp.TmpM.Value, + temp.TmpL.Binary, temp.TmpL.Value) + + // 3. 湿度解析 + t.Log("\n=== 湿度解析 ===") + humidity, err := protocol.GetHumidity() + if err != nil { + t.Fatalf("获取湿度失败: %v", err) + } + t.Logf("湿度: %d%% (raw: 0x%X)", humidity.Complete.Value, humidity.Complete.RawValue) + t.Logf("HM_H: %s (0x%X), HM_L: %s (0x%X)", + humidity.HmH.Binary, humidity.HmH.Value, + humidity.HmL.Binary, humidity.HmL.Value) + + // 4. 风速解析 + t.Log("\n=== 风速解析 ===") + windSpeed, err := protocol.GetWindSpeed() + if err != nil { + t.Fatalf("获取风速失败: %v", err) + } + t.Logf("风速: %.5f m/s (raw: 0x%X)", windSpeed.Complete.Value, windSpeed.Complete.RawValue) + t.Logf("WSP_FLAG: %v", windSpeed.WspFlag.Value) + t.Logf("WSP_H: %s (0x%X), WSP_L: %s (0x%X)", + windSpeed.WspH.Binary, windSpeed.WspH.Value, + windSpeed.WspL.Binary, windSpeed.WspL.Value) + + // 5. 风向解析 + t.Log("\n=== 风向解析 ===") + windDir, err := protocol.GetWindDirection() + if err != nil { + t.Fatalf("获取风向失败: %v", err) + } + t.Logf("风向: %.1f° (raw: 0x%X)", windDir.Complete.Degree, windDir.Complete.Value) + t.Logf("DirH: %s (0x%X), DirM: %s (0x%X), DirL: %s (0x%X)", + windDir.DirH.Binary, windDir.DirH.Value, + windDir.DirM.Binary, windDir.DirM.Value, + windDir.DirL.Binary, windDir.DirL.Value) + + // 6. 降雨量解析 + t.Log("\n=== 降雨量解析 ===") + rainfall, err := protocol.GetRainfall() + if err != nil { + t.Fatalf("获取降雨量失败: %v", err) + } + t.Logf("降雨量: %.3f mm (raw: 0x%X)", rainfall.Complete.Value, rainfall.Complete.RawValue) + t.Logf("原始字节: %02X %02X", data[8], data[9]) + + // 7. 光照解析 + t.Log("\n=== 光照解析 ===") + light, err := protocol.GetLight() + if err != nil { + t.Fatalf("获取光照失败: %v", err) + } + t.Logf("光照: %.1f lux (raw: 0x%X)", light.Complete.Value, light.Complete.RawValue) + t.Logf("原始字节: %02X %02X %02X", data[12], data[13], data[14]) + + // 8. UV指数解析 + t.Log("\n=== UV指数解析 ===") + uv, err := protocol.GetUVIndex() + if err != nil { + t.Fatalf("获取UV指数失败: %v", err) + } + t.Logf("UV指数: %.1f uW/c㎡ (raw: 0x%X)", uv.Complete.Value, uv.Complete.RawValue) + t.Logf("原始字节: %02X %02X", data[10], data[11]) + + // 9. 气压解析 + t.Log("\n=== 气压解析 ===") + pressure, err := protocol.GetPressure() + if err != nil { + t.Fatalf("获取气压失败: %v", err) + } + t.Logf("气压: %.2f hPa (raw: 0x%X)", pressure.Complete.Value, pressure.Complete.RawValue) + t.Logf("原始字节: %02X %02X %02X", data[17], data[18], data[19]) + + // 10. 阵风速度解析 + t.Log("\n=== 阵风速度解析 ===") + gust, err := protocol.GetGustSpeed() + if err != nil { + t.Fatalf("获取阵风速度失败: %v", err) + } + t.Logf("阵风速度: %.2f m/s (raw: 0x%X)", gust.Complete.Value, gust.Complete.RawValue) + t.Logf("原始字节: %02X", data[7]) + + // 11. 创建RS485协议解析器并解析数据 + t.Log("\n=== RS485协议解析 ===") + rs485Protocol := NewRS485Protocol(data) + rs485Data, err := rs485Protocol.ParseRS485Data() + if err != nil { + t.Fatalf("RS485数据解析失败: %v", err) + } + + t.Logf("温度: %.2f°C", rs485Data.Temperature) + t.Logf("湿度: %.1f%%", rs485Data.Humidity) + t.Logf("风速: %.2f m/s", rs485Data.WindSpeed) + t.Logf("风向: %.1f°", rs485Data.WindDirection) + t.Logf("降雨量: %.3f mm", rs485Data.Rainfall) + t.Logf("光照: %.1f lux", rs485Data.Light) + t.Logf("紫外线: %.1f", rs485Data.UV) + t.Logf("气压: %.2f hPa", rs485Data.Pressure) +}