test: Listen on UDP port 10006 and parse weather station data
This commit is contained in:
parent
8312ff3008
commit
1fa4319879
47
config/config.go
Normal file
47
config/config.go
Normal file
@ -0,0 +1,47 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
UDPPort int `yaml:"udp_port"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig `yaml:"server"`
|
||||
}
|
||||
|
||||
var (
|
||||
instance *Config
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func GetConfig() *Config {
|
||||
once.Do(func() {
|
||||
instance = &Config{}
|
||||
err := instance.loadConfig()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("加载配置文件失败: %v", err))
|
||||
}
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
func (c *Config) loadConfig() error {
|
||||
data, err := os.ReadFile("config/config.yaml")
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(data, c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
2
config/config.yaml
Normal file
2
config/config.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
server:
|
||||
udp_port: 10006
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module weatherstation
|
||||
|
||||
go 1.21
|
||||
|
||||
require gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
3
go.sum
Normal file
3
go.sum
Normal file
@ -0,0 +1,3 @@
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
46
main.go
Normal file
46
main.go
Normal file
@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"weatherstation/config"
|
||||
"weatherstation/model"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
data := string(buffer[:n])
|
||||
log.Printf("从 %s 接收到 %d 字节数据", addr.String(), n)
|
||||
|
||||
weatherData, err := model.ParseWeatherData(data)
|
||||
if err != nil {
|
||||
log.Printf("解析数据失败: %v", err)
|
||||
log.Printf("原始数据: %s", data)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println("成功解析气象站数据:")
|
||||
fmt.Println(weatherData)
|
||||
}
|
||||
}
|
||||
216
model/weather_data.go
Normal file
216
model/weather_data.go
Normal file
@ -0,0 +1,216 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WeatherData struct {
|
||||
StationID string
|
||||
Password string
|
||||
TempF float64
|
||||
Humidity int
|
||||
DewpointF float64
|
||||
WindchillF float64
|
||||
WindDir int
|
||||
WindSpeedMph float64
|
||||
WindGustMph float64
|
||||
RainIn float64
|
||||
DailyRainIn float64
|
||||
WeeklyRainIn float64
|
||||
MonthlyRainIn float64
|
||||
YearlyRainIn float64
|
||||
TotalRainIn float64
|
||||
SolarRadiation float64
|
||||
UV int
|
||||
IndoorTempF float64
|
||||
IndoorHumidity int
|
||||
AbsBarometerIn float64
|
||||
BarometerIn float64
|
||||
LowBattery bool
|
||||
SoftwareType string
|
||||
DateUTC string
|
||||
Action string
|
||||
RealTime int
|
||||
RTFreq int
|
||||
}
|
||||
|
||||
func ParseWeatherData(data string) (*WeatherData, error) {
|
||||
if !strings.Contains(data, "GET /weatherstation/updateweatherstation.php") {
|
||||
return nil, fmt.Errorf("不是气象站数据")
|
||||
}
|
||||
|
||||
urlStart := strings.Index(data, "GET ")
|
||||
if urlStart == -1 {
|
||||
return nil, fmt.Errorf("无法找到URL开始位置")
|
||||
}
|
||||
|
||||
httpVersionEnd := strings.Index(data, " HTTP")
|
||||
if httpVersionEnd == -1 {
|
||||
return nil, fmt.Errorf("无法找到URL结束位置")
|
||||
}
|
||||
|
||||
urlString := data[urlStart+4 : httpVersionEnd]
|
||||
queryStart := strings.Index(urlString, "?")
|
||||
if queryStart == -1 {
|
||||
return nil, fmt.Errorf("无法找到查询参数")
|
||||
}
|
||||
|
||||
queryString := urlString[queryStart+1:]
|
||||
values, err := url.ParseQuery(queryString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析查询参数失败: %v", err)
|
||||
}
|
||||
|
||||
wd := &WeatherData{}
|
||||
|
||||
wd.StationID = values.Get("ID")
|
||||
wd.Password = values.Get("PASSWORD")
|
||||
wd.DateUTC = values.Get("dateutc")
|
||||
wd.SoftwareType = values.Get("softwaretype")
|
||||
wd.Action = values.Get("action")
|
||||
|
||||
if tempF, err := strconv.ParseFloat(values.Get("tempf"), 64); err == nil {
|
||||
wd.TempF = tempF
|
||||
}
|
||||
|
||||
if humidity, err := strconv.Atoi(values.Get("humidity")); err == nil {
|
||||
wd.Humidity = humidity
|
||||
}
|
||||
|
||||
if dewpointF, err := strconv.ParseFloat(values.Get("dewptf"), 64); err == nil {
|
||||
wd.DewpointF = dewpointF
|
||||
}
|
||||
|
||||
if windchillF, err := strconv.ParseFloat(values.Get("windchillf"), 64); err == nil {
|
||||
wd.WindchillF = windchillF
|
||||
}
|
||||
|
||||
if windDir, err := strconv.Atoi(values.Get("winddir")); err == nil {
|
||||
wd.WindDir = windDir
|
||||
}
|
||||
|
||||
if windSpeedMph, err := strconv.ParseFloat(values.Get("windspeedmph"), 64); err == nil {
|
||||
wd.WindSpeedMph = windSpeedMph
|
||||
}
|
||||
|
||||
if windGustMph, err := strconv.ParseFloat(values.Get("windgustmph"), 64); err == nil {
|
||||
wd.WindGustMph = windGustMph
|
||||
}
|
||||
|
||||
if rainIn, err := strconv.ParseFloat(values.Get("rainin"), 64); err == nil {
|
||||
wd.RainIn = rainIn
|
||||
}
|
||||
|
||||
if dailyRainIn, err := strconv.ParseFloat(values.Get("dailyrainin"), 64); err == nil {
|
||||
wd.DailyRainIn = dailyRainIn
|
||||
}
|
||||
|
||||
if weeklyRainIn, err := strconv.ParseFloat(values.Get("weeklyrainin"), 64); err == nil {
|
||||
wd.WeeklyRainIn = weeklyRainIn
|
||||
}
|
||||
|
||||
if monthlyRainIn, err := strconv.ParseFloat(values.Get("monthlyrainin"), 64); err == nil {
|
||||
wd.MonthlyRainIn = monthlyRainIn
|
||||
}
|
||||
|
||||
if yearlyRainIn, err := strconv.ParseFloat(values.Get("yearlyrainin"), 64); err == nil {
|
||||
wd.YearlyRainIn = yearlyRainIn
|
||||
}
|
||||
|
||||
if totalRainIn, err := strconv.ParseFloat(values.Get("totalrainin"), 64); err == nil {
|
||||
wd.TotalRainIn = totalRainIn
|
||||
}
|
||||
|
||||
if solarRadiation, err := strconv.ParseFloat(values.Get("solarradiation"), 64); err == nil {
|
||||
wd.SolarRadiation = solarRadiation
|
||||
}
|
||||
|
||||
if uv, err := strconv.Atoi(values.Get("UV")); err == nil {
|
||||
wd.UV = uv
|
||||
}
|
||||
|
||||
if indoorTempF, err := strconv.ParseFloat(values.Get("indoortempf"), 64); err == nil {
|
||||
wd.IndoorTempF = indoorTempF
|
||||
}
|
||||
|
||||
if indoorHumidity, err := strconv.Atoi(values.Get("indoorhumidity")); err == nil {
|
||||
wd.IndoorHumidity = indoorHumidity
|
||||
}
|
||||
|
||||
if absBarometerIn, err := strconv.ParseFloat(values.Get("absbaromin"), 64); err == nil {
|
||||
wd.AbsBarometerIn = absBarometerIn
|
||||
}
|
||||
|
||||
if barometerIn, err := strconv.ParseFloat(values.Get("baromin"), 64); err == nil {
|
||||
wd.BarometerIn = barometerIn
|
||||
}
|
||||
|
||||
if lowBatt, err := strconv.Atoi(values.Get("lowbatt")); err == nil {
|
||||
wd.LowBattery = lowBatt != 0
|
||||
}
|
||||
|
||||
if realTime, err := strconv.Atoi(values.Get("realtime")); err == nil {
|
||||
wd.RealTime = realTime
|
||||
}
|
||||
|
||||
if rtFreq, err := strconv.Atoi(values.Get("rtfreq")); err == nil {
|
||||
wd.RTFreq = rtFreq
|
||||
}
|
||||
|
||||
return wd, nil
|
||||
}
|
||||
|
||||
func (w *WeatherData) String() string {
|
||||
return fmt.Sprintf(`
|
||||
站点ID: %s
|
||||
温度: %.1f°F (%.1f°C)
|
||||
湿度: %d%%
|
||||
露点: %.1f°F (%.1f°C)
|
||||
风寒指数: %.1f°F (%.1f°C)
|
||||
风向: %d°
|
||||
风速: %.2f mph (%.2f km/h)
|
||||
阵风: %.2f mph (%.2f km/h)
|
||||
降雨量: %.3f 英寸 (%.2f mm)
|
||||
日降雨量: %.3f 英寸 (%.2f mm)
|
||||
周降雨量: %.3f 英寸 (%.2f mm)
|
||||
月降雨量: %.3f 英寸 (%.2f mm)
|
||||
年降雨量: %.3f 英寸 (%.2f mm)
|
||||
总降雨量: %.3f 英寸 (%.2f mm)
|
||||
太阳辐射: %.2f W/m²
|
||||
紫外线指数: %d
|
||||
室内温度: %.1f°F (%.1f°C)
|
||||
室内湿度: %d%%
|
||||
绝对气压: %.3f 英寸汞柱 (%.2f hPa)
|
||||
相对气压: %.3f 英寸汞柱 (%.2f hPa)
|
||||
低电量: %v
|
||||
软件类型: %s
|
||||
日期UTC: %s
|
||||
`,
|
||||
w.StationID,
|
||||
w.TempF, (w.TempF-32)*5/9,
|
||||
w.Humidity,
|
||||
w.DewpointF, (w.DewpointF-32)*5/9,
|
||||
w.WindchillF, (w.WindchillF-32)*5/9,
|
||||
w.WindDir,
|
||||
w.WindSpeedMph, w.WindSpeedMph*1.60934,
|
||||
w.WindGustMph, w.WindGustMph*1.60934,
|
||||
w.RainIn, w.RainIn*25.4,
|
||||
w.DailyRainIn, w.DailyRainIn*25.4,
|
||||
w.WeeklyRainIn, w.WeeklyRainIn*25.4,
|
||||
w.MonthlyRainIn, w.MonthlyRainIn*25.4,
|
||||
w.YearlyRainIn, w.YearlyRainIn*25.4,
|
||||
w.TotalRainIn, w.TotalRainIn*25.4,
|
||||
w.SolarRadiation,
|
||||
w.UV,
|
||||
w.IndoorTempF, (w.IndoorTempF-32)*5/9,
|
||||
w.IndoorHumidity,
|
||||
w.AbsBarometerIn, w.AbsBarometerIn*33.8639,
|
||||
w.BarometerIn, w.BarometerIn*33.8639,
|
||||
w.LowBattery,
|
||||
w.SoftwareType,
|
||||
w.DateUTC,
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user