| | |
| | | |
| | | import ( |
| | | "bytes" |
| | | "context" |
| | | "encoding/binary" |
| | | "encoding/hex" |
| | | "encoding/json" |
| | | "fmt" |
| | | "io/ioutil" |
| | |
| | | "gat1400Exchange/config" |
| | | "gat1400Exchange/models" |
| | | "gat1400Exchange/pkg/logger" |
| | | "gat1400Exchange/rfid" |
| | | "gat1400Exchange/util" |
| | | |
| | | "github.com/gin-gonic/gin" |
| | | "github.com/google/uuid" |
| | | "golang.org/x/text/encoding/simplifiedchinese" |
| | | "golang.org/x/text/transform" |
| | | ) |
| | | |
| | | type ElevatorStatus struct { |
| | | type A1SetFloorFrame struct { |
| | | Header [16]byte // Fixed 16-byte header |
| | | Command byte // 1 byte command |
| | | MAC [6]byte // 6 bytes MAC address |
| | | DataLength uint16 // 2 bytes data length |
| | | Data []byte // Data area, length defined by DataLength |
| | | Checksum uint16 // 2 bytes CRC16 checksum |
| | | } |
| | | |
| | | // Convert frame to byte slice |
| | | func (f *A1SetFloorFrame) toBytes(includeChecksum bool) []byte { |
| | | buf := new(bytes.Buffer) |
| | | binary.Write(buf, binary.LittleEndian, f.Header) |
| | | binary.Write(buf, binary.LittleEndian, f.Command) |
| | | binary.Write(buf, binary.LittleEndian, f.MAC) |
| | | binary.Write(buf, binary.LittleEndian, f.DataLength) |
| | | buf.Write(f.Data) |
| | | if includeChecksum { |
| | | binary.Write(buf, binary.LittleEndian, f.Checksum) |
| | | } |
| | | return buf.Bytes() |
| | | } |
| | | |
| | | type A1ElevatorStatus struct { |
| | | TotalFloors int `json:"TotalFloors"` |
| | | Floor int `json:"Floor"` |
| | | FloorName string `json:"FloorName"` |
| | |
| | | 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 A1Elevator struct { |
| | | Name string `json:"Name"` |
| | | IP string `json:"IP"` |
| | | Status A1ElevatorStatus `json:"Status"` |
| | | Alarm []interface{} `json:"Alarm"` // You might want to define a specific type for alarms |
| | | } |
| | | |
| | | type A1ElevatorData struct { |
| | | Elevator []Elevator `json:"Elevator"` |
| | | Elevator []A1Elevator `json:"Elevator"` |
| | | } |
| | | |
| | | const ( |
| | |
| | | RunUp |
| | | RunDown |
| | | ) |
| | | |
| | | var ElevatorRunState int |
| | | var ElevatorRunFloor string |
| | | var RunningCorrectTaskId string |
| | | |
| | | func A1CorrectFloor() { |
| | | if RunningCorrectTaskId != "" || config.RFIDConf.EPC == "" { |
| | | return |
| | | } |
| | | |
| | | taskId := uuid.New().String() |
| | | RunningCorrectTaskId = taskId |
| | | |
| | | rfidReader := rfid.NewReader(config.RFIDConf.DevName, 115200, 5) |
| | | defer rfidReader.CloseSerial() |
| | | |
| | | err := rfidReader.OpenSerial() |
| | | if err != nil { |
| | | logger.Error(err.Error()) |
| | | return |
| | | } |
| | | |
| | | isFind, err := rfidReader.ScanSpecificEPC(config.RFIDConf.EPC, 5) |
| | | if isFind && RunningCorrectTaskId == taskId { |
| | | frame := NewA1SetFloorFrame(config.NVCSConf.Mac, config.RFIDConf.Position) |
| | | address := "192.168.10.253:50000" |
| | | err := A1SendFrame(frame, address) |
| | | if err != nil { |
| | | logger.Warn(err.Error()) |
| | | } else { |
| | | logger.Debug("The floor has been calibrated.") |
| | | } |
| | | } |
| | | } |
| | | |
| | | func calculateCRC16(data []byte) uint16 { |
| | | var crc16 uint16 = 0xFFFF |
| | | |
| | | for i := 0; i < len(data); i++ { |
| | | crc16 ^= uint16(data[i]) |
| | | for j := 0; j < 8; j++ { |
| | | if crc16&0x0001 != 0 { |
| | | crc16 >>= 1 |
| | | crc16 ^= 0xA001 |
| | | } else { |
| | | crc16 >>= 1 |
| | | } |
| | | } |
| | | } |
| | | |
| | | return crc16 |
| | | } |
| | | |
| | | // Create a new frame based on provided data |
| | | func NewA1SetFloorFrame(macAddr string, floor uint8) *A1SetFloorFrame { |
| | | b, err := hex.DecodeString(macAddr) |
| | | if err != nil { |
| | | return nil |
| | | } |
| | | |
| | | if len(b) != 6 { |
| | | return nil |
| | | } |
| | | |
| | | var mac [6]byte |
| | | copy(mac[:], b) |
| | | |
| | | //b, err = hex.DecodeString(floor) |
| | | frame := &A1SetFloorFrame{ |
| | | Header: [16]byte{0x45, 0x4c, 0x45, 0x56, 0x41, 0x54, 0x4f, 0x52, 0x2d, 0x53, 0x45, 0x4e, 0x53, 0x4f, 0x52, 0x00}, |
| | | Command: 0x0c, |
| | | MAC: mac, |
| | | DataLength: 1, |
| | | Data: []byte{floor}, |
| | | } |
| | | |
| | | frame.Checksum = calculateCRC16(frame.toBytes(false)) // Calculate CRC without including the checksum itself |
| | | |
| | | return frame |
| | | } |
| | | |
| | | func A1SendFrame(frame *A1SetFloorFrame, address string) error { |
| | | conn, err := net.Dial("udp", address) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | defer conn.Close() |
| | | |
| | | // Send frame |
| | | _, err = conn.Write(frame.toBytes(true)) |
| | | return err |
| | | } |
| | | |
| | | // 对接网络视频字符叠加器,接收udp发送的楼层信息, 更新device地址 |
| | | func NVCSA1UDPServer() { |
| | |
| | | |
| | | logger.Info("UDP server listening on port %s...", port) |
| | | |
| | | var runState string |
| | | rfidReader := rfid.NewReader(config.RFIDConf.DevName, 115200, 5) |
| | | if config.RFIDConf.ReadFloor { |
| | | defer rfidReader.CloseSerial() |
| | | |
| | | err := rfidReader.OpenSerial() |
| | | if err != nil { |
| | | logger.Error("open rfid. %s", err.Error()) |
| | | return |
| | | } |
| | | |
| | | ctx, _ := context.WithCancel(context.Background()) |
| | | |
| | | go func() { |
| | | err := rfidReader.ReadEPCData(ctx) |
| | | if err != nil { |
| | | logger.Error("rfid read. %s", err.Error()) |
| | | } |
| | | }() |
| | | } |
| | | |
| | | var lastFloor int |
| | | var lastSaveTime int64 |
| | | // 无限循环等待接收数据 |
| | | for { |
| | | // 创建一个缓冲区来存储接收的数据 |
| | |
| | | continue |
| | | } |
| | | |
| | | // 记录电梯运行状态, 只记录上行和下行 |
| | | if data.Elevator[0].Status.RunDir == RunUp { |
| | | runState = "上" |
| | | } else if data.Elevator[0].Status.RunDir == RunDown { |
| | | runState = "下" |
| | | elevator := data.Elevator[0] |
| | | |
| | | var runStateStr string |
| | | if config.NVCSConf.RunState { |
| | | if elevator.Status.RunDir == RunUp { |
| | | runStateStr = "上" |
| | | } else if elevator.Status.RunDir == RunDown { |
| | | runStateStr = "下" |
| | | } |
| | | } |
| | | |
| | | // 已到最下层 |
| | | if data.Elevator[0].Status.Floor == 0 { |
| | | runState = "上" |
| | | } |
| | | if data.Elevator[0].Status.Floor == data.Elevator[0].Status.TotalFloors { |
| | | runState = "下" |
| | | if !config.NVCSConf.RunState { |
| | | runStateStr = "" |
| | | } |
| | | |
| | | // 设置osd 格式 "1F上 固 枪" |
| | | if config.NVCSConf.OSD != "" { |
| | | floorText := fmt.Sprintf("%s%s %s", data.Elevator[0].Status.FloorName, runState, config.NVCSConf.OSD) |
| | | // correct floor when elevator stopped. |
| | | //if elevator.Status.RunDir == 0 { |
| | | // go A1CorrectFloor() |
| | | //} else { |
| | | // RunningCorrectTaskId = "" |
| | | //} |
| | | |
| | | // 调用hik api 将文字添加到osd的左下角 |
| | | AddFloorToOSD(floorText) |
| | | } |
| | | |
| | | if data.Elevator[0].Status.RunDir > 0 { |
| | | if lastFloor == elevator.Status.Floor && (time.Now().Unix()-lastSaveTime < 2) { |
| | | continue |
| | | } |
| | | |
| | | elevator := data.Elevator[0] |
| | | lastFloor = elevator.Status.Floor |
| | | lastSaveTime = time.Now().Unix() |
| | | |
| | | // 程序部署在设备端, 字符叠加器上报的名称允许为空. 在云端, 名称必须与摄像机相同 |
| | | 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"), |
| | | ElevatorRunState = elevator.Status.RunDir |
| | | ElevatorRunFloor = elevator.Status.FloorName |
| | | if config.RFIDConf.ReadFloor && rfidReader.EPCData != "" { |
| | | ElevatorRunFloor, _ = rfid.Parse2Floor(rfidReader.EPCData) |
| | | logger.Debug("rfid read epc floor %s", ElevatorRunFloor) |
| | | if elevator.Status.RunDir == RunStop && ElevatorRunFloor != elevator.Status.FloorName { |
| | | logger.Warn("A floor error has occurred rfid epc %s, nvcs floor %s", ElevatorRunFloor, elevator.Status.FloorName) |
| | | } |
| | | } |
| | | |
| | | err = d.Save() |
| | | if err != nil { |
| | | logger.Warn("Device position update error:%s", err.Error()) |
| | | // 设置osd 格式 "1F上 固 枪" |
| | | if config.NVCSConf.OSD != "" { |
| | | floorText := fmt.Sprintf("%s%s %s", ElevatorRunFloor, runStateStr, config.NVCSConf.OSD) |
| | | |
| | | // 调用hik api 将文字添加到osd的左下角 |
| | | AddFloorToOSD(floorText) |
| | | } |
| | | |
| | | go func() { |
| | | var d = models.Positions{ |
| | | DeviceId: elevator.Name, |
| | | Pos: ElevatorRunFloor, |
| | | RunDir: ElevatorRunState, |
| | | 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()) |
| | | } |
| | | }() |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | func NVCSA2WebServer() { |
| | | // 先获取总楼层数, 记录最高层 |
| | | GetA2ElevatorConfig() |
| | | var runState string |
| | | //GetA2ElevatorConfig() |
| | | |
| | | r := gin.Default() |
| | | |
| | | r.POST("/", func(c *gin.Context) { |
| | | var req A2ElevatorData |
| | | var runState string |
| | | var iRunState int |
| | | |
| | | err := c.BindJSON(&req) |
| | | if err != nil { |
| | | c.JSON(http.StatusBadRequest, nil) |
| | |
| | | |
| | | logger.Debug("Received A2 report data %+v", req) |
| | | |
| | | // 记录电梯运行状态, 只记录上行和下行 |
| | | // 记录电梯运行状态 |
| | | if strings.Contains(req.State.Status, "上") { |
| | | runState = "上" |
| | | iRunState = RunUp |
| | | } else if strings.Contains(req.State.Status, "下") { |
| | | runState = "下" |
| | | iRunState = RunDown |
| | | } |
| | | |
| | | // 已到最下层 |
| | | if req.State.Floor == A2BottomFloor { |
| | | runState = "上" |
| | | } |
| | | if req.State.Floor == A2TopFloor { |
| | | runState = "下" |
| | | ElevatorRunState = iRunState |
| | | ElevatorRunFloor = req.State.Floor |
| | | |
| | | //// 已到最下层 |
| | | //if req.State.Floor == A2BottomFloor { |
| | | // runState = "上" |
| | | // iRunState = RunUp |
| | | //} |
| | | //if req.State.Floor == A2TopFloor { |
| | | // runState = "下" |
| | | // iRunState = RunDown |
| | | //} |
| | | |
| | | if !config.NVCSConf.RunState { |
| | | runState = "" |
| | | } |
| | | |
| | | if config.NVCSConf.OSD != "" { |
| | |
| | | var d = models.Positions{ |
| | | DeviceId: req.Id, |
| | | Pos: req.State.Floor, |
| | | RunDir: iRunState, |
| | | 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()) |
| | | } |
| | | } |
| | | |
| | | type A3ElevatorData struct { |
| | | Id string `json:"id"` |
| | | Time string `json:"time"` |
| | | Total int `json:"total"` |
| | | Status int `json:"status"` |
| | | Floor int `json:"floor"` |
| | | } |
| | | |
| | | func NVCSA3WebServer() { |
| | | r := gin.Default() |
| | | |
| | | r.POST("/", func(c *gin.Context) { |
| | | var req A3ElevatorData |
| | | var runState string |
| | | var iRunState int |
| | | |
| | | err := c.BindJSON(&req) |
| | | if err != nil { |
| | | c.JSON(http.StatusBadRequest, nil) |
| | | return |
| | | } |
| | | |
| | | iRunState = req.Status |
| | | ElevatorRunState = req.Status |
| | | ElevatorRunFloor = fmt.Sprintf("%dF", req.Floor) |
| | | logger.Debug("Received A2 report data %+v", req) |
| | | |
| | | // 记录电梯运行状态 |
| | | if iRunState == RunUp { |
| | | runState = "上" |
| | | } else if iRunState == RunDown { |
| | | runState = "下" |
| | | } |
| | | |
| | | if !config.NVCSConf.RunState { |
| | | runState = "" |
| | | } |
| | | |
| | | if config.NVCSConf.OSD != "" { |
| | | floorText := fmt.Sprintf("%dF%s %s", req.Floor, runState, config.NVCSConf.OSD) |
| | | |
| | | // 调用hik api 将文字添加到osd的左下角 |
| | | AddFloorToOSD(floorText) |
| | | } |
| | | |
| | | var d = models.Positions{ |
| | | DeviceId: req.Id, |
| | | Pos: fmt.Sprintf("%dF", req.Floor), |
| | | RunDir: iRunState, |
| | | CreateTime: time.Now().Unix(), |
| | | TimeString: time.Now().Format("2006-01-02 15:04:05"), |
| | | } |
| | |
| | | if config.NVCSConf.Model == "A1" { |
| | | go NVCSA1UDPServer() |
| | | } |
| | | |
| | | if config.NVCSConf.Model == "A2" { |
| | | go NVCSA2WebServer() |
| | | } |
| | | |
| | | if config.NVCSConf.Model == "A3" { |
| | | go NVCSA3WebServer() |
| | | } |
| | | } |