package service import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net" "net/http" "strings" "time" "gat1400Exchange/config" "gat1400Exchange/models" "gat1400Exchange/pkg/logger" "gat1400Exchange/util" "github.com/gin-gonic/gin" "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" ) type ElevatorStatus struct { TotalFloors int `json:"TotalFloors"` Floor int `json:"Floor"` FloorName string `json:"FloorName"` RunDir int `json:"RunDir"` Speed string `json:"Speed"` } type Elevator struct { Name string `json:"Name"` IP string `json:"IP"` Status ElevatorStatus `json:"Status"` Alarm []interface{} `json:"Alarm"` // You might want to define a specific type for alarms } type A1ElevatorData struct { Elevator []Elevator `json:"Elevator"` } const ( RunStop = iota RunUp RunDown ) // 对接网络视频字符叠加器,接收udp发送的楼层信息, 更新device地址 func NVCSA1UDPServer() { // 指定监听的端口 port := config.ServeConf.Port // 创建一个UDP地址 address, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%s", port)) if err != nil { logger.Error("Error resolving address:", err) return } // 创建一个UDP连接 conn, err := net.ListenUDP("udp", address) if err != nil { logger.Error("Error listening:", err) return } defer conn.Close() logger.Info("UDP server listening on port %s...", port) var runState string // 无限循环等待接收数据 for { // 创建一个缓冲区来存储接收的数据 buffer := make([]byte, 256) // 从连接中读取数据 numBytes, clientAddr, err := conn.ReadFromUDP(buffer) if err != nil { logger.Warn("Error reading from UDP connection:%s", err.Error()) continue } // 设备上传的中文数据为GBK编码, 转换为utf8 decoder := simplifiedchinese.GBK.NewDecoder() reader := transform.NewReader(bytes.NewReader(buffer[:numBytes]), decoder) decodedBytes, err := ioutil.ReadAll(reader) var data A1ElevatorData err = json.Unmarshal(decodedBytes, &data) if err != nil { logger.Warn("ElevatorData unmarshal error:%s", err.Error()) continue } logger.Debug("Received %d bytes from %s, %+v", numBytes, clientAddr, data) if len(data.Elevator) == 0 { continue } // 记录电梯运行状态, 只记录上行和下行 if data.Elevator[0].Status.RunDir == RunUp { runState = "上" } else if data.Elevator[0].Status.RunDir == RunDown { runState = "下" } // 已到最下层 if data.Elevator[0].Status.Floor == 0 { runState = "上" } if data.Elevator[0].Status.Floor == data.Elevator[0].Status.TotalFloors { runState = "下" } // 设置osd 格式 "1F上 固 枪" if config.NVCSConf.OSD != "" { floorText := fmt.Sprintf("%s%s %s", data.Elevator[0].Status.FloorName, runState, config.NVCSConf.OSD) // 调用hik api 将文字添加到osd的左下角 AddFloorToOSD(floorText) } if data.Elevator[0].Status.RunDir > 0 { continue } elevator := data.Elevator[0] // 程序部署在设备端, 字符叠加器上报的名称允许为空. 在云端, 名称必须与摄像机相同 if elevator.Name == "" { elevator.Name = "1" } var d = models.Positions{ DeviceId: elevator.Name, Pos: elevator.Status.FloorName, CreateTime: time.Now().Unix(), TimeString: time.Now().Format("2006-01-02 15:04:05"), } err = d.Save() if err != nil { logger.Warn("Device position update error:%s", err.Error()) } } } /* A2 款 数据上报格式 { "id": "10c8a1b0051607361c", "State": { "Floor": "-1", "Floor_flag": "已校准", "JZ_flag": "已校准,", "JZ_i": 7, "Pressure": "99766", "Speed": "0.000", "Status": "停止1", "TFloor": 7, "T_acc": "0.062", "X_acc": "1.175", "Y_acc": "-1.129", "Z_acc": "8.344" } */ type A2ElevatorData struct { Id string `json:"id"` State struct { Floor string `json:"Floor"` Status string `json:"Status"` TFloor int64 `json:"TFloor"` Speed string `json:"Speed"` } `json:"State"` } type A2ElevatorConfig struct { FloorData []string `json:"floordata"` } var A2TopFloor string var A2BottomFloor string func GetA2ElevatorConfig() { url := "http://192.168.10.253/cgi-bin/liftnum.cgi" payload := []byte("{\"display\":1}") header := map[string]string{ "Cookie": "eyJuYW1lIjoiYWRtaW4iLCAicGFzZCI6ImFkbWluMTIzIn0=", } rsp, err := util.HttpPost(url, header, payload) if err != nil { logger.Warn("Get A2 floor data failure,%s", err.Error()) return } var configData A2ElevatorConfig err = json.Unmarshal(rsp, &configData) if err != nil { logger.Warn("Unmarshal A2 floor data failure,%s", err.Error()) return } if len(configData.FloorData) > 0 { A2BottomFloor, A2TopFloor = configData.FloorData[0], configData.FloorData[len(configData.FloorData)-1] } logger.Info("A2 floor config total:%d, bottomFloor:%s, topFloor:%s", len(configData.FloorData), A2BottomFloor, A2TopFloor) } func NVCSA2WebServer() { // 先获取总楼层数, 记录最高层 GetA2ElevatorConfig() var runState string r := gin.Default() r.POST("/", func(c *gin.Context) { var req A2ElevatorData err := c.BindJSON(&req) if err != nil { c.JSON(http.StatusBadRequest, nil) return } logger.Debug("Received A2 report data %+v", req) // 记录电梯运行状态, 只记录上行和下行 if strings.Contains(req.State.Status, "上") { runState = "上" } else if strings.Contains(req.State.Status, "下") { runState = "下" } // 已到最下层 if req.State.Floor == A2BottomFloor { runState = "上" } if req.State.Floor == A2TopFloor { runState = "下" } if config.NVCSConf.OSD != "" { floorText := fmt.Sprintf("%s%s %s", req.State.Floor, runState, config.NVCSConf.OSD) // 调用hik api 将文字添加到osd的左下角 AddFloorToOSD(floorText) } var d = models.Positions{ DeviceId: req.Id, Pos: req.State.Floor, CreateTime: time.Now().Unix(), TimeString: time.Now().Format("2006-01-02 15:04:05"), } err = d.Save() if err != nil { logger.Warn("Device position update error:%s", err.Error()) } c.JSON(http.StatusOK, "ok") }) err := r.Run(fmt.Sprintf(":%s", config.NVCSConf.Port)) if err != nil { logger.Warn("Start NVCS WebServer error, %s", err.Error()) } } func StartNVCSServer() { if config.NVCSConf.Model == "A1" { go NVCSA1UDPServer() } if config.NVCSConf.Model == "A2" { go NVCSA2WebServer() } }