diff --git a/config.yaml b/config.yaml index c56989c..14bf81b 100644 --- a/config.yaml +++ b/config.yaml @@ -7,4 +7,12 @@ database: user: "weatheruser" password: "yourpassword" dbname: "weatherdb" - sslmode: "disable" \ No newline at end of file + sslmode: "disable" + +heartbeat: + interval: 5 + message: "Hello" + +device_check: + interval: 5 + message: "Hello" \ No newline at end of file diff --git a/config/config.go b/config/config.go index c4b313a..a6feb23 100644 --- a/config/config.go +++ b/config/config.go @@ -21,9 +21,21 @@ type DatabaseConfig struct { SSLMode string `yaml:"sslmode"` } +type HeartbeatConfig struct { + Interval int `yaml:"interval"` + Message string `yaml:"message"` +} + +type DeviceCheckConfig struct { + Interval int `yaml:"interval"` + Message string `yaml:"message"` +} + type Config struct { - Server ServerConfig `yaml:"server"` - Database DatabaseConfig `yaml:"database"` + Server ServerConfig `yaml:"server"` + Database DatabaseConfig `yaml:"database"` + Heartbeat HeartbeatConfig `yaml:"heartbeat"` + DeviceCheck DeviceCheckConfig `yaml:"device_check"` } var ( diff --git a/main.go b/main.go index d5067a9..772ab77 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,8 @@ import ( "weatherstation/config" "weatherstation/model" + + "github.com/gin-gonic/gin" ) type UTF8Writer struct { @@ -29,9 +31,8 @@ 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, "�") + s = strings.ToValidUTF8(s, "") return w.w.Write([]byte(s)) } @@ -40,78 +41,64 @@ func setupLogger() { 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 main() { - setupLogger() - +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) @@ -121,40 +108,85 @@ func main() { } } +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 { @@ -163,9 +195,7 @@ func asciiDump(data []byte) string { result.WriteString(".") } } - result.WriteString("\n") } - return result.String() } diff --git a/model/device.go b/model/device.go new file mode 100644 index 0000000..009485b --- /dev/null +++ b/model/device.go @@ -0,0 +1,44 @@ +package model + +import ( + "net" + "sync" + "time" +) + +type Device struct { + IP string + LastSeen time.Time + StationID string +} + +var ( + devices = make(map[string]*Device) + deviceMutex sync.RWMutex +) + +func RegisterDevice(stationID string, addr net.Addr) { + ip, _, _ := net.SplitHostPort(addr.String()) + + deviceMutex.Lock() + defer deviceMutex.Unlock() + + devices[stationID] = &Device{ + IP: ip, + LastSeen: time.Now(), + StationID: stationID, + } +} + +func GetOnlineDevices() []*Device { + deviceMutex.RLock() + defer deviceMutex.RUnlock() + + result := make([]*Device, 0, len(devices)) + for _, device := range devices { + if time.Since(device.LastSeen) < 10*time.Minute { + result = append(result, device) + } + } + return result +}