weather-station/main.go
2025-07-24 16:36:29 +08:00

202 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
}