低空智控平台 后端go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

129 lines
3.6 KiB

package main
import (
"log"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
)
// dockSpec 描述机巢及其直接模拟上报的绑定无人机。
type dockSpec struct {
DockID string
Name string
Location string
DroneSN string
DroneName string
Latitude float64
Longitude float64
DroneLat float64
DroneLon float64
Flying bool
Battery int
Alarm bool
}
func main() {
broker := envOr("MOCK_MQTT_BROKER", "tcp://roll.jiagutech.com:1883")
username := envOr("MOCK_MQTT_USERNAME", "laic")
password := envOr("MOCK_MQTT_PASSWORD", "")
clientIDPrefix := envOr("MOCK_MQTT_CLIENT_ID_PREFIX", "mock-dock-")
videoFile := resolveVideoFile(envOr("MOCK_VIDEO_FILE", "mock/mock.mp4"))
if info, err := os.Stat(videoFile); err != nil {
log.Printf("视频素材不可用(仅影响真实推流和原始视频上传): %s: %v", videoFile, err)
} else {
log.Printf("视频素材: %s (%d bytes)", videoFile, info.Size())
}
specs := []dockSpec{
{
DockID: "dock-1", Name: "青山湖 01 号机巢", Location: "青山湖科技城 A 区",
DroneSN: "JG-UAV-001", DroneName: "巡检无人机 001",
Latitude: 30.262438, Longitude: 119.802631, DroneLat: 30.262438, DroneLon: 119.802631,
Battery: 86,
},
{
DockID: "dock-2", Name: "青山湖 02 号机巢", Location: "青山湖科技城 B 区",
DroneSN: "JG-UAV-002", DroneName: "巡检无人机 002",
Latitude: 30.255917, Longitude: 119.818264, DroneLat: 30.256500, DroneLon: 119.818900,
Flying: true, Battery: 72,
},
{
DockID: "dock-3", Name: "青山湖 03 号机巢", Location: "青山湖科技城 C 区",
DroneSN: "JG-UAV-003", DroneName: "巡检无人机 003",
Latitude: 30.248610, Longitude: 119.791450, DroneLat: 30.248610, DroneLon: 119.791450,
Battery: 41, Alarm: true,
},
}
docks := make([]*MockDock, 0, len(specs))
for _, spec := range selectDockSpecs(specs, os.Getenv("MOCK_DOCKS")) {
dock := newMockDock(spec, clientIDPrefix, videoFile)
if err := dock.start(broker, username, password); err != nil {
log.Fatalf("启动机巢 %s 失败: %v", spec.DockID, err)
}
docks = append(docks, dock)
}
log.Println("模拟设备已启动(机巢 MQTT 直接上报机巢与无人机数据),按 Ctrl+C 退出")
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("正在退出...")
for _, dock := range docks {
dock.stop()
}
time.Sleep(200 * time.Millisecond)
}
// resolveVideoFile first checks the current working directory, then the executable directory.
// This keeps the default path usable both with `go run` and with a copied binary.
func resolveVideoFile(path string) string {
if filepath.IsAbs(path) {
return path
}
if _, err := os.Stat(path); err == nil {
return path
}
if exe, err := os.Executable(); err == nil {
candidate := filepath.Join(filepath.Dir(exe), path)
if _, err := os.Stat(candidate); err == nil {
return candidate
}
}
return path
}
func selectDockSpecs(specs []dockSpec, selected string) []dockSpec {
if strings.TrimSpace(selected) == "" {
return specs
}
allowed := make(map[string]struct{})
for _, dockID := range strings.Split(selected, ",") {
if dockID = strings.TrimSpace(dockID); dockID != "" {
allowed[dockID] = struct{}{}
}
}
selectedSpecs := make([]dockSpec, 0, len(allowed))
for _, spec := range specs {
if _, ok := allowed[spec.DockID]; ok {
selectedSpecs = append(selectedSpecs, spec)
}
}
if len(selectedSpecs) == 0 {
log.Fatalf("MOCK_DOCKS 未匹配任何设备: %s", selected)
}
return selectedSpecs
}
func envOr(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}