package main import ( "bufio" "encoding/hex" "fmt" "io" "log" "net" "os" "path/filepath" "strings" "time" "unicode/utf8" "weatherstation/config" "weatherstation/model" "github.com/gin-gonic/gin" ) 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)) } func setupLogger() { logDir := "log" if _, err := os.Stat(logDir); os.IsNotExist(err) { os.MkdirAll(logDir, 0755) } currentTime := time.Now() logFileName := filepath.Join(logDir, fmt.Sprintf("%s.log", currentTime.Format("2006-01-02"))) logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Fatalf("无法创建日志文件: %v", err) } bufferedWriter := bufio.NewWriter(logFile) utf8Writer := NewUTF8Writer(bufferedWriter) go func() { for { time.Sleep(1 * time.Second) bufferedWriter.Flush() } }() 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] data := string(rawData) log.Printf("从 %s 接收到 %d 字节数据", addr.String(), n) weatherData, err := model.ParseWeatherData(data) if err != nil { log.Printf("解析数据失败: %v", err) hexDump := hexDump(rawData) log.Printf("原始码流(十六进制):\n%s", hexDump) asciiDump := asciiDump(rawData) log.Printf("ASCII码:\n%s", asciiDump) continue } log.Println("成功解析气象站数据:") log.Println(weatherData) // 注册设备 model.RegisterDevice(weatherData.StationID, addr) err = model.SaveWeatherData(weatherData, data) if err != nil { log.Printf("保存数据到数据库失败: %v", err) } else { log.Printf("数据已成功保存到数据库") } } } func startDeviceCheck() { cfg := config.GetConfig() ticker := time.NewTicker(time.Duration(cfg.DeviceCheck.Interval) * time.Minute) defer ticker.Stop() for range ticker.C { devices := model.GetOnlineDevices() log.Printf("当前在线设备数: %d", len(devices)) for _, device := range devices { sendUDPMessage(device.IP, cfg.DeviceCheck.Message) } } } func sendUDPMessage(ip string, message string) { addr, err := net.ResolveUDPAddr("udp", ip+":10006") if err != nil { log.Printf("解析UDP地址失败: %v", err) return } conn, err := net.DialUDP("udp", nil, addr) if err != nil { log.Printf("连接UDP失败: %v", err) return } defer conn.Close() _, err = conn.Write([]byte(message)) if err != nil { log.Printf("发送UDP消息失败: %v", err) return } log.Printf("成功向 %s 发送消息: %s", ip, message) } func main() { setupLogger() go startUDP() go startDeviceCheck() r := gin.Default() r.LoadHTMLGlob("templates/*") r.Static("/static", "static") r.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", gin.H{}) }) r.Run(":10007") } 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() }