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 { // 解析RS485数据 hexStr := strings.ReplaceAll(hexDump, "\n", " ") parseHexData(hexStr) // 注册设备 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()) } } 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") } } } } // 暂时不保存到数据库 // err = model.SaveWeatherData(data, string(rawData)) // if err != nil { // log.Printf("保存数据到数据库失败: %v", err) // } else { // log.Printf("数据已成功保存到数据库") // } } } 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 } // 检查数据有效性 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) }