package main import ( "bufio" "encoding/hex" "fmt" "io" "log" "net" "os" "path/filepath" "strings" "sync" "time" "unicode/utf8" "weatherstation/config" "weatherstation/model" ) type UTF8Writer struct { w io.Writer } func NewUTF8Writer(w io.Writer) *UTF8Writer { return &UTF8Writer{w: w} } func (w *UTF8Writer) Write(p []byte) (n int, err error) { if utf8.Valid(p) { return w.w.Write(p) } s := string(p) s = strings.ToValidUTF8(s, "") return w.w.Write([]byte(s)) } var ( logFile *os.File logFileMutex sync.Mutex currentLogDay int ) func getLogFileName() string { currentTime := time.Now() return filepath.Join("log", fmt.Sprintf("%s.log", currentTime.Format("2006-01-02"))) } func openLogFile() (*os.File, error) { logDir := "log" if _, err := os.Stat(logDir); os.IsNotExist(err) { os.MkdirAll(logDir, 0755) } logFileName := getLogFileName() return os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) } func setupLogger() { var err error logFile, err = openLogFile() if err != nil { log.Fatalf("无法创建日志文件: %v", err) } currentLogDay = time.Now().Day() bufferedWriter := bufio.NewWriter(logFile) utf8Writer := NewUTF8Writer(bufferedWriter) go func() { for { time.Sleep(1 * time.Second) logFileMutex.Lock() bufferedWriter.Flush() now := time.Now() if now.Day() != currentLogDay { oldLogFile := logFile logFile, err = openLogFile() if err != nil { log.Printf("无法创建新日志文件: %v", err) } else { oldLogFile.Close() currentLogDay = now.Day() bufferedWriter = bufio.NewWriter(logFile) utf8Writer = NewUTF8Writer(bufferedWriter) log.SetOutput(io.MultiWriter(os.Stdout, utf8Writer)) log.Println("日志文件已轮转") } } logFileMutex.Unlock() } }() multiWriter := io.MultiWriter(os.Stdout, utf8Writer) log.SetOutput(multiWriter) log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) } func startUDP() { cfg := config.GetConfig() err := model.InitDB() if err != nil { log.Fatalf("初始化数据库失败: %v", err) } defer model.CloseDB() addr := fmt.Sprintf(":%d", cfg.Server.UDPPort) conn, err := net.ListenPacket("udp", addr) if err != nil { log.Fatalf("无法监听UDP端口 %d: %v", cfg.Server.UDPPort, err) } defer conn.Close() log.Printf("UDP服务器已启动,监听端口 %d...", cfg.Server.UDPPort) buffer := make([]byte, 2048) for { n, addr, err := conn.ReadFrom(buffer) if err != nil { log.Printf("读取数据错误: %v", err) continue } rawData := buffer[:n] log.Printf("从 %s 接收到 %d 字节数据", addr.String(), n) hexDump := hexDump(rawData) log.Printf("原始码流(十六进制):\n%s", hexDump) asciiDump := asciiDump(rawData) log.Printf("ASCII码:\n%s", asciiDump) // 检查数据是否为RS485格式 if len(rawData) == 25 && rawData[0] == 0x24 { log.Println("=== 检测到RS485设备数据 ===") // 生成源码字符串(用于日志记录) sourceHex := strings.ReplaceAll(strings.TrimSpace(hexDump), "\n", " ") log.Printf("源码: %s", sourceHex) // 解析RS485数据 protocol := model.NewProtocol(rawData) rs485Protocol := model.NewRS485Protocol(rawData) // 获取设备ID idParts, err := protocol.GetCompleteID() if err != nil { log.Printf("获取设备ID失败: %v", err) continue } // 解析RS485数据 rs485Data, err := rs485Protocol.ParseRS485Data() if err != nil { log.Printf("解析RS485数据失败: %v", err) continue } // 添加设备ID和时间戳 rs485Data.DeviceID = idParts.Complete.Hex rs485Data.ReceivedAt = time.Now() rs485Data.RawDataHex = sourceHex // 打印解析结果到日志 log.Println("=== RS485解析结果 ===") log.Printf("设备ID: RS485-%s", rs485Data.DeviceID) log.Printf("温度: %.2f°C", rs485Data.Temperature) log.Printf("湿度: %.1f%%", rs485Data.Humidity) log.Printf("风速: %.5f m/s", rs485Data.WindSpeed) log.Printf("风向: %.1f°", rs485Data.WindDirection) log.Printf("降雨量: %.3f mm", rs485Data.Rainfall) log.Printf("光照: %.1f lux", rs485Data.Light) log.Printf("紫外线: %.1f", rs485Data.UV) log.Printf("气压: %.2f hPa", rs485Data.Pressure) log.Printf("接收时间: %s", rs485Data.ReceivedAt.Format("2006-01-02 15:04:05")) // 注册设备 stationID := fmt.Sprintf("RS485-%s", rs485Data.DeviceID) model.RegisterDevice(stationID, addr) log.Printf("设备 %s 已注册,IP: %s", stationID, addr.String()) // 可选:保存到数据库 // err = model.SaveWeatherData(rs485Data, string(rawData)) // if err != nil { // log.Printf("保存数据到数据库失败: %v", err) // } else { // log.Printf("数据已成功保存到数据库") // } } else { // 尝试解析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") } } } } } } func getDeviceTypeString(deviceType model.DeviceType) string { switch deviceType { case model.DeviceTypeWIFI: return "WIFI" case model.DeviceTypeRS485: return "RS485" default: return "未知" } } func main() { // 检查是否有命令行参数 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 { var result strings.Builder for i := 0; i < len(data); i += 16 { end := i + 16 if end > len(data) { end = len(data) } chunk := data[i:end] hexStr := hex.EncodeToString(chunk) for j := 0; j < len(hexStr); j += 2 { if j+2 <= len(hexStr) { result.WriteString(strings.ToUpper(hexStr[j : j+2])) result.WriteString(" ") } } result.WriteString("\n") } return result.String() } func asciiDump(data []byte) string { var result strings.Builder for i := 0; i < len(data); i += 64 { end := i + 64 if end > len(data) { end = len(data) } chunk := data[i:end] for _, b := range chunk { if b >= 32 && b <= 126 { result.WriteByte(b) } else { result.WriteString(".") } } result.WriteString("\n") } 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 } // 打印原始数据 log.Println("=== 原始数据分析 ===") log.Printf("输入的十六进制字符串: %s", hexStr) log.Printf("解析后的字节数组长度: %d", len(data)) log.Printf("字节数组内容: %v", data) // 按索引打印每个字节 for i, b := range data { log.Printf("索引[%2d]: 0x%02X (%d)", i, b, b) } // 检查数据有效性 if !model.ValidateRS485Data(data) { log.Printf("无效的RS485数据格式: 长度=%d, 起始字节=%02X", len(data), data[0]) return } log.Println("\n=== 使用Protocol.go标准解析 ===") // 创建协议解析器 protocol := model.NewProtocol(data) rs485Protocol := model.NewRS485Protocol(data) // 1. 解析设备ID log.Println("--- 设备ID解析 ---") idParts, err := protocol.GetCompleteID() if err != nil { log.Printf("获取设备ID失败: %v", err) return } log.Printf("HSB (索引21): 0x%02X = %d", data[21], data[21]) log.Printf("MSB (索引22): 0x%02X = %d", data[22], data[22]) log.Printf("LSB (索引1): 0x%02X = %d", data[1], data[1]) log.Printf("完整设备ID: %s", idParts.Complete.Hex) // 2. 解析温度 log.Println("--- 温度解析 ---") temp, err := protocol.GetTemperature() if err != nil { log.Printf("获取温度失败: %v", err) } else { log.Printf("TMP_H (bit29-31): %s (0x%X)", temp.TmpH.Binary, temp.TmpH.Value) log.Printf("TMP_M (bit32-35): %s (0x%X)", temp.TmpM.Binary, temp.TmpM.Value) log.Printf("TMP_L (bit36-39): %s (0x%X)", temp.TmpL.Binary, temp.TmpL.Value) log.Printf("原始值: 0x%X = %d", temp.Complete.RawValue, temp.Complete.RawValue) log.Printf("温度: %.2f°C", temp.Complete.Value) } // 3. 解析湿度 log.Println("--- 湿度解析 ---") humidity, err := protocol.GetHumidity() if err != nil { log.Printf("获取湿度失败: %v", err) } else { log.Printf("HM_H (bit40-43): %s (0x%X)", humidity.HmH.Binary, humidity.HmH.Value) log.Printf("HM_L (bit44-47): %s (0x%X)", humidity.HmL.Binary, humidity.HmL.Value) log.Printf("原始值: 0x%02X = %d", humidity.Complete.RawValue, humidity.Complete.RawValue) log.Printf("湿度: %d%%", humidity.Complete.Value) } // 4. 解析风速 log.Println("--- 风速解析 ---") windSpeed, err := protocol.GetWindSpeed() if err != nil { log.Printf("获取风速失败: %v", err) } else { log.Printf("WSP_FLAG: %v", windSpeed.WspFlag.Value) log.Printf("WSP_H (bit48-51): %s (0x%X)", windSpeed.WspH.Binary, windSpeed.WspH.Value) log.Printf("WSP_L (bit52-55): %s (0x%X)", windSpeed.WspL.Binary, windSpeed.WspL.Value) log.Printf("原始值: 0x%X = %d", windSpeed.Complete.RawValue, windSpeed.Complete.RawValue) log.Printf("风速: %.5f m/s", windSpeed.Complete.Value) } // 5. 解析风向 log.Println("--- 风向解析 ---") windDir, err := protocol.GetWindDirection() if err != nil { log.Printf("获取风向失败: %v", err) } else { log.Printf("DIR_H: %s (0x%X)", windDir.DirH.Binary, windDir.DirH.Value) log.Printf("DIR_M: %s (0x%X)", windDir.DirM.Binary, windDir.DirM.Value) log.Printf("DIR_L: %s (0x%X)", windDir.DirL.Binary, windDir.DirL.Value) log.Printf("原始值: 0x%X = %d", windDir.Complete.Value, windDir.Complete.Value) log.Printf("风向: %.1f°", windDir.Complete.Degree) } // 6. 解析降雨量 log.Println("--- 降雨量解析 ---") rainfall, err := protocol.GetRainfall() if err != nil { log.Printf("获取降雨量失败: %v", err) } else { log.Printf("原始值: 0x%X = %d", rainfall.Complete.RawValue, rainfall.Complete.RawValue) log.Printf("降雨量: %.3f mm", rainfall.Complete.Value) } // 7. 解析光照 log.Println("--- 光照解析 ---") light, err := protocol.GetLight() if err != nil { log.Printf("获取光照失败: %v", err) } else { log.Printf("原始值: 0x%X = %d", light.Complete.RawValue, light.Complete.RawValue) log.Printf("光照: %.1f lux", light.Complete.Value) } // 8. 解析UV指数 log.Println("--- UV指数解析 ---") uv, err := protocol.GetUVIndex() if err != nil { log.Printf("获取UV指数失败: %v", err) } else { log.Printf("原始值: 0x%X = %d", uv.Complete.RawValue, uv.Complete.RawValue) log.Printf("UV指数: %.1f uW/c㎡", uv.Complete.Value) } // 9. 解析气压 log.Println("--- 气压解析 ---") pressure, err := protocol.GetPressure() if err != nil { log.Printf("获取气压失败: %v", err) } else { log.Printf("原始值: 0x%X = %d", pressure.Complete.RawValue, pressure.Complete.RawValue) log.Printf("气压: %.2f hPa", pressure.Complete.Value) } log.Println("\n=== RS485协议统一解析结果 ===") // 使用修正后的RS485解析 rs485Data, err := rs485Protocol.ParseRS485Data() if err != nil { log.Printf("解析RS485数据失败: %v", err) return } else { rs485Data.DeviceID = idParts.Complete.Hex rs485Data.ReceivedAt = time.Now() rs485Data.RawDataHex = fmt.Sprintf("%X", data) log.Printf("设备ID: RS485-%s", rs485Data.DeviceID) log.Printf("温度: %.2f°C", rs485Data.Temperature) log.Printf("湿度: %.1f%%", rs485Data.Humidity) log.Printf("风速: %.5f m/s", rs485Data.WindSpeed) log.Printf("风向: %.1f°", rs485Data.WindDirection) log.Printf("降雨量: %.3f mm", rs485Data.Rainfall) log.Printf("光照: %.1f lux", rs485Data.Light) log.Printf("紫外线: %.1f", rs485Data.UV) log.Printf("气压: %.2f hPa", rs485Data.Pressure) } }