2个文件已删除
8个文件已添加
1 文件已重命名
5个文件已修改
| | |
| | | package controller |
| | | |
| | | import ( |
| | | "gat1400Exchange/service" |
| | | "gat1400Exchange/nvcs" |
| | | "github.com/gin-gonic/gin" |
| | | "net/http" |
| | | ) |
| | |
| | | |
| | | func (n NVCSController) RunStatus(c *gin.Context) { |
| | | var runStatus = make(map[string]string, 0) |
| | | runStatus["floor"] = service.ElevatorRunFloor |
| | | runStatus["floor"] = nvcs.ElevatorRunFloor |
| | | |
| | | switch service.ElevatorRunState { |
| | | switch nvcs.ElevatorRunState { |
| | | case 0: |
| | | runStatus["status"] = "停" |
| | | case 1: |
| | |
| | | import ( |
| | | "context" |
| | | "fmt" |
| | | "gat1400Exchange/nvcs" |
| | | "net/http" |
| | | "os" |
| | | "os/signal" |
| | |
| | | go client.Init1400Client(ctx) |
| | | |
| | | // 启动网络视频字符叠加器服务 |
| | | go service.StartNVCSServer() |
| | | go nvcs.StartNVCSServer() |
| | | |
| | | go service.InitSubscribeNotificationTasks() |
| | | |
New file |
| | |
| | | package nvcs |
| | | |
| | | import ( |
| | | "bytes" |
| | | "encoding/binary" |
| | | "encoding/hex" |
| | | "encoding/json" |
| | | "fmt" |
| | | "io/ioutil" |
| | | "net" |
| | | "time" |
| | | |
| | | "gat1400Exchange/config" |
| | | "gat1400Exchange/pkg/logger" |
| | | "gat1400Exchange/rfid" |
| | | |
| | | "github.com/google/uuid" |
| | | "golang.org/x/text/encoding/simplifiedchinese" |
| | | "golang.org/x/text/transform" |
| | | ) |
| | | |
| | | 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"` |
| | | RunDir int `json:"RunDir"` |
| | | Speed string `json:"Speed"` |
| | | } |
| | | |
| | | 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 []a1Elevator `json:"Elevator"` |
| | | } |
| | | |
| | | var gRunningCorrectTaskId string |
| | | |
| | | func a1CorrectFloor() { |
| | | if gRunningCorrectTaskId != "" || config.RFIDConf.EPC == "" { |
| | | return |
| | | } |
| | | |
| | | taskId := uuid.New().String() |
| | | gRunningCorrectTaskId = 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 && gRunningCorrectTaskId == 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 a1UDPServer() { |
| | | // 指定监听的端口 |
| | | 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) |
| | | |
| | | // 无限循环等待接收数据 |
| | | 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 |
| | | } |
| | | |
| | | elevator := data.Elevator[0] |
| | | |
| | | // 程序部署在设备端, 字符叠加器上报的名称允许为空. 在云端, 名称必须与摄像机相同 |
| | | if elevator.Name == "" { |
| | | elevator.Name = "1" |
| | | } |
| | | |
| | | var runState = elevatorRunData{ |
| | | Device: elevator.Name, |
| | | Timestamp: time.Now().Unix(), |
| | | Floor: elevator.Status.FloorName, |
| | | RunState: elevator.Status.RunDir, |
| | | } |
| | | |
| | | // correct floor when elevator stopped. |
| | | //if elevator.Status.RunDir == 0 { |
| | | // go A1CorrectFloor() |
| | | //} else { |
| | | // RunningCorrectTaskId = "" |
| | | //} |
| | | |
| | | queue.enqueue(runState) |
| | | } |
| | | } |
New file |
| | |
| | | package nvcs |
| | | |
| | | import ( |
| | | "encoding/json" |
| | | "fmt" |
| | | "net/http" |
| | | "strings" |
| | | "time" |
| | | |
| | | "gat1400Exchange/config" |
| | | "gat1400Exchange/pkg/logger" |
| | | "gat1400Exchange/util" |
| | | |
| | | "github.com/gin-gonic/gin" |
| | | ) |
| | | |
| | | /* |
| | | 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 a2WebServer() { |
| | | // 先获取总楼层数, 记录最高层 |
| | | //GetA2ElevatorConfig() |
| | | |
| | | r := gin.Default() |
| | | |
| | | r.POST("/", func(c *gin.Context) { |
| | | var req a2ElevatorData |
| | | var iRunState int |
| | | |
| | | 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, "上") { |
| | | iRunState = RunUp |
| | | } else if strings.Contains(req.State.Status, "下") { |
| | | iRunState = RunDown |
| | | } |
| | | |
| | | var runState = elevatorRunData{ |
| | | Device: req.Id, |
| | | Timestamp: time.Now().Unix(), |
| | | Floor: req.State.Floor, |
| | | RunState: iRunState, |
| | | } |
| | | |
| | | queue.enqueue(runState) |
| | | |
| | | 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()) |
| | | } |
| | | } |
New file |
| | |
| | | package nvcs |
| | | |
| | | import ( |
| | | "fmt" |
| | | "net/http" |
| | | "time" |
| | | |
| | | "gat1400Exchange/config" |
| | | "gat1400Exchange/pkg/logger" |
| | | |
| | | "github.com/gin-gonic/gin" |
| | | ) |
| | | |
| | | /* |
| | | 485平层传感器 + 485转以太网模块 |
| | | */ |
| | | type a3ElevatorData struct { |
| | | Id string `json:"id"` |
| | | Time string `json:"time"` |
| | | Total int `json:"total"` |
| | | Status int `json:"status"` |
| | | Floor int `json:"floor"` |
| | | } |
| | | |
| | | func a3WebServer() { |
| | | r := gin.Default() |
| | | |
| | | r.POST("/", func(c *gin.Context) { |
| | | var req a3ElevatorData |
| | | |
| | | err := c.BindJSON(&req) |
| | | if err != nil { |
| | | c.JSON(http.StatusBadRequest, nil) |
| | | return |
| | | } |
| | | |
| | | logger.Debug("Received A3 report data %+v", req) |
| | | |
| | | floor := fmt.Sprintf("%dF", req.Floor) |
| | | |
| | | var runState = elevatorRunData{ |
| | | Device: req.Id, |
| | | Timestamp: time.Now().Unix(), |
| | | Floor: floor, |
| | | RunState: req.Status, |
| | | } |
| | | |
| | | queue.enqueue(runState) |
| | | |
| | | 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()) |
| | | } |
| | | } |
New file |
| | |
| | | package nvcs |
| | | |
| | | import ( |
| | | "fmt" |
| | | "time" |
| | | |
| | | "gat1400Exchange/config" |
| | | "gat1400Exchange/models" |
| | | "gat1400Exchange/pkg/logger" |
| | | ) |
| | | |
| | | type simpleCache struct { |
| | | data map[int64]elevatorRunData |
| | | expiration time.Duration |
| | | } |
| | | |
| | | func newCache(expiration time.Duration) *simpleCache { |
| | | return &simpleCache{ |
| | | data: make(map[int64]elevatorRunData), |
| | | expiration: expiration, |
| | | } |
| | | } |
| | | |
| | | // 存储数据到缓存中 |
| | | func (c *simpleCache) store(data elevatorRunData) { |
| | | if config.RFIDConf.ReadFloor && gRFIDFloor != data.Floor { |
| | | if data.RunState == RunStop { |
| | | logger.Warn("A floor error has occurred rfid epc %s, nvcs floor %s", gRFIDFloor, data.Floor) |
| | | } |
| | | |
| | | data.Floor = gRFIDFloor |
| | | } |
| | | |
| | | // 如果楼层相同,并且数据在1秒内,则忽略 |
| | | if existingData, ok := c.data[data.Timestamp]; ok { |
| | | if existingData.Floor == data.Floor { |
| | | return |
| | | } |
| | | } |
| | | |
| | | // 数据库保存一份 |
| | | go func() { |
| | | var d = models.Positions{ |
| | | DeviceId: data.Device, |
| | | Pos: data.Floor, |
| | | RunDir: data.RunState, |
| | | 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()) |
| | | } |
| | | }() |
| | | |
| | | // 写OSD |
| | | var runStateStr string |
| | | if config.NVCSConf.RunState { |
| | | if data.RunState == RunUp { |
| | | runStateStr = "上" |
| | | } else if data.RunState == RunDown { |
| | | runStateStr = "下" |
| | | } |
| | | } |
| | | |
| | | // 设置osd 格式 "1F上 固 枪" |
| | | if config.NVCSConf.OSD != "" { |
| | | floorText := fmt.Sprintf("%s%s %s", data.Floor, runStateStr, config.NVCSConf.OSD) |
| | | |
| | | // 调用hik api 将文字添加到osd的左下角 |
| | | go addFloorToOSD(floorText) |
| | | } |
| | | |
| | | c.data[data.Timestamp] = data |
| | | } |
| | | |
| | | // 删除过期数据 |
| | | func (c *simpleCache) cleanExpired() { |
| | | for t := range c.data { |
| | | if time.Since(time.Unix(t, 0)) > c.expiration { |
| | | delete(c.data, t) |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package nvcs |
| | | |
| | | import ( |
| | | "context" |
| | | "gat1400Exchange/config" |
| | | "time" |
| | | ) |
| | | |
| | | const ( |
| | | RunStop = iota |
| | | RunUp |
| | | RunDown |
| | | ) |
| | | |
| | | // 数据结构 |
| | | type elevatorRunData struct { |
| | | Device string |
| | | Timestamp int64 |
| | | Floor string |
| | | RunState int |
| | | } |
| | | |
| | | var queue = newChQueue(100) |
| | | var cache = newCache(5 * time.Minute) |
| | | |
| | | func StartNVCSServer(ctx context.Context) { |
| | | |
| | | go listenQueue() |
| | | |
| | | if config.RFIDConf.ReadFloor { |
| | | go readRFID(ctx) |
| | | } |
| | | |
| | | if config.NVCSConf.Model == "A1" { |
| | | go a1UDPServer() |
| | | } |
| | | |
| | | if config.NVCSConf.Model == "A2" { |
| | | go a2WebServer() |
| | | } |
| | | |
| | | if config.NVCSConf.Model == "A3" { |
| | | go a3WebServer() |
| | | } |
| | | } |
| | | |
| | | func listenQueue() { |
| | | for { |
| | | data := queue.dequeue() |
| | | cache.store(data) |
| | | |
| | | // 清理过期数据 |
| | | cache.cleanExpired() |
| | | } |
| | | } |
File was renamed from service/osd.go |
| | |
| | | package service |
| | | package nvcs |
| | | |
| | | import ( |
| | | "encoding/xml" |
| | | "gat1400Exchange/pkg/logger" |
| | | dac "github.com/xinsnake/go-http-digest-auth-client" |
| | | "io/ioutil" |
| | | |
| | | "gat1400Exchange/pkg/logger" |
| | | |
| | | dac "github.com/xinsnake/go-http-digest-auth-client" |
| | | ) |
| | | |
| | | // 设置其他字符的第一个, 位置固定位7 |
| | | // 摄像机信息暂时固定, 避免泄露 |
| | | const ( |
| | | HikISAPIOverlaySetUrl = "/ISAPI/System/Video/inputs/channels/1/overlays/text/7" |
| | | CameraWebAddr = "http://192.168.10.11:40080" |
| | | CameraWebUser = "admin" |
| | | CameraWebPassword = "a1234567" |
| | | hikISAPIOverlay7SetUrl = "/ISAPI/System/Video/inputs/channels/1/overlays/text/7" |
| | | cameraWebAddr = "http://192.168.10.11:40080" |
| | | cameraWebUser = "admin" |
| | | cameraWebPassword = "a1234567" |
| | | ) |
| | | |
| | | var overlayText TextOverlay |
| | | var overlayText textOverlay |
| | | |
| | | type TextOverlay struct { |
| | | type textOverlay struct { |
| | | Id int64 `xml:"id"` |
| | | Enabled bool `xml:"enabled"` |
| | | PositionX int64 `xml:"positionX"` |
| | |
| | | IsPersistentText bool `xml:"isPersistentText"` |
| | | } |
| | | |
| | | func AddFloorToOSD(osdText string) { |
| | | overlay7Url := CameraWebAddr + HikISAPIOverlaySetUrl |
| | | func hikISAPIRequest(username, password, method, url, body string) ([]byte, error) { |
| | | dr := dac.NewRequest(username, password, method, url, body) |
| | | resp, err := dr.Execute() |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | defer resp.Body.Close() |
| | | |
| | | return ioutil.ReadAll(resp.Body) |
| | | } |
| | | |
| | | func addFloorToOSD(osdText string) { |
| | | overlay7Url := cameraWebAddr + hikISAPIOverlay7SetUrl |
| | | |
| | | // 获取左下角第一个字符的位置 |
| | | if overlayText.DisplayText == "" { |
| | | rsp, err := ISAPIRequest(CameraWebUser, CameraWebPassword, "GET", overlay7Url, "") |
| | | rsp, err := hikISAPIRequest(cameraWebUser, cameraWebPassword, "GET", overlay7Url, "") |
| | | if err != nil { |
| | | logger.Warn("Get osd info failure") |
| | | return |
| | |
| | | |
| | | overlayText.DisplayText = osdText |
| | | body, _ := xml.Marshal(overlayText) |
| | | _, err := ISAPIRequest(CameraWebUser, CameraWebPassword, "PUT", overlay7Url, string(body)) |
| | | _, err := hikISAPIRequest(cameraWebUser, cameraWebPassword, "PUT", overlay7Url, string(body)) |
| | | if err != nil { |
| | | logger.Warn("Camera osd set failure!!") |
| | | return |
| | | } |
| | | } |
| | | |
| | | func ISAPIRequest(username, password, method, url, body string) ([]byte, error) { |
| | | dr := dac.NewRequest(username, password, method, url, body) |
| | | resp, err := dr.Execute() |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | defer resp.Body.Close() |
| | | |
| | | return ioutil.ReadAll(resp.Body) |
| | | } |
New file |
| | |
| | | package nvcs |
| | | |
| | | type chQueue struct { |
| | | data chan elevatorRunData |
| | | } |
| | | |
| | | func newChQueue(size int) *chQueue { |
| | | return &chQueue{ |
| | | data: make(chan elevatorRunData, size), |
| | | } |
| | | } |
| | | |
| | | func (q *chQueue) enqueue(data elevatorRunData) { |
| | | q.data <- data |
| | | } |
| | | |
| | | func (q *chQueue) dequeue() elevatorRunData { |
| | | data := <-q.data |
| | | return data |
| | | } |
New file |
| | |
| | | package nvcs |
| | | |
| | | import ( |
| | | "context" |
| | | "time" |
| | | |
| | | "gat1400Exchange/config" |
| | | "gat1400Exchange/pkg/logger" |
| | | "gat1400Exchange/rfid" |
| | | ) |
| | | |
| | | var gRFIDFloor string |
| | | |
| | | func readRFID(ctx context.Context) { |
| | | rfidReader := rfid.NewReader(config.RFIDConf.DevName, 115200, 5) |
| | | defer rfidReader.CloseSerial() |
| | | |
| | | err := rfidReader.OpenSerial() |
| | | if err != nil { |
| | | logger.Error("open rfid. %s", err.Error()) |
| | | return |
| | | } |
| | | |
| | | go func() { |
| | | err := rfidReader.ReadEPCData(ctx) |
| | | if err != nil { |
| | | logger.Error("rfid read. %s", err.Error()) |
| | | } |
| | | }() |
| | | |
| | | for { |
| | | select { |
| | | case <-ctx.Done(): |
| | | return |
| | | default: |
| | | if rfidReader.Alive { |
| | | floor, _ := rfid.Parse2Floor(rfidReader.EPCData) |
| | | |
| | | if floor != gRFIDFloor { |
| | | gRFIDFloor = floor |
| | | logger.Debug("rfid read epc floor %s", gRFIDFloor) |
| | | } |
| | | |
| | | } |
| | | |
| | | time.Sleep(200 * time.Millisecond) |
| | | } |
| | | } |
| | | } |
| | |
| | | import ( |
| | | "encoding/base64" |
| | | "encoding/json" |
| | | "gat1400Exchange/nvcs" |
| | | "time" |
| | | |
| | | "gat1400Exchange/client" |
| | |
| | | "gat1400Exchange/models" |
| | | "gat1400Exchange/pkg" |
| | | "gat1400Exchange/pkg/logger" |
| | | "gat1400Exchange/service" |
| | | "gat1400Exchange/util" |
| | | "gat1400Exchange/vo" |
| | | ) |
| | |
| | | var dbPos models.Positions |
| | | if err := dbPos.FindMovePosition(faceAppearTime.Unix()+5, floor); err == nil { |
| | | switch dbPos.RunDir { |
| | | case service.RunUp: |
| | | case nvcs.RunUp: |
| | | runDir = "in" |
| | | case service.RunDown: |
| | | case nvcs.RunDown: |
| | | runDir = "out" |
| | | case service.RunStop: |
| | | case nvcs.RunStop: |
| | | runDir = "" |
| | | } |
| | | |
| | |
| | | var dbPos models.Positions |
| | | if err := dbPos.FindMovePosition(faceAppearTime.Unix()+5, floor); err == nil { |
| | | switch dbPos.RunDir { |
| | | case service.RunUp: |
| | | case nvcs.RunUp: |
| | | runDir = "1" |
| | | case service.RunDown: |
| | | case nvcs.RunDown: |
| | | runDir = "2" |
| | | case service.RunStop: |
| | | case nvcs.RunStop: |
| | | runDir = "0" |
| | | } |
| | | |
New file |
| | |
| | | package rfid |
| | | |
| | | import ( |
| | | "time" |
| | | |
| | | "github.com/tarm/serial" |
| | | ) |
| | | |
| | | func OpenSerial(devName string, baud, timeout int) (*serial.Port, error){ |
| | | config := &serial.Config{ |
| | | Name: devName, |
| | | Baud: baud, |
| | | ReadTimeout: time.Duration(timeout) * time.Second, |
| | | } |
| | | |
| | | return serial.OpenPort(config) |
| | | } |
| | | |
| | | func CloseSerial(port *serial.Port) error { |
| | | return port.Close() |
| | | } |
| | |
| | | |
| | | const ( |
| | | ControlWordEPCReadResponse6C uint32 = 0x00011200 |
| | | ControlWordConnectionStatus uint32 = 0x00011112 |
| | | ControlWordDeviceInfo uint32 = 0x00010100 |
| | | ) |
| | | |
| | | func Parse2Floor(input string) (string, error) { |
| | |
| | | |
| | | func NewReader(devName string, baud, readDuration int) *Reader { |
| | | return &Reader{ |
| | | DevName: devName, |
| | | Baud: baud, |
| | | ReadDuration: readDuration, |
| | | DevName: devName, |
| | | Baud: baud, |
| | | ReadTimeout: readDuration, |
| | | Alive: true, |
| | | HeartbeatTime: time.Now().Unix(), |
| | | } |
| | | } |
| | | |
| | | type Reader struct { |
| | | DevName string |
| | | Baud int |
| | | ReadDuration int |
| | | EPCData string |
| | | ReadTimestamp int64 |
| | | ReadTimeout int |
| | | Alive bool |
| | | HeartbeatTime int64 |
| | | DevPort *serial.Port |
| | | } |
| | | |
| | |
| | | func (r *Reader) ReadResponse() (int, error) { |
| | | buf := make([]byte, 1024) // 根据协议最大数据长度调整缓冲区 |
| | | n, err := r.DevPort.Read(buf) |
| | | fmt.Printf("Recive message %x\n", buf[:n]) |
| | | fmt.Printf("Recive message %X\n", buf[:n]) |
| | | |
| | | return n, err |
| | | } |
| | |
| | | |
| | | defer r.StopAutoRead() |
| | | |
| | | stop := time.After(time.Duration(r.ReadDuration) * time.Second) |
| | | stop := time.After(time.Duration(r.ReadTimeout) * time.Second) |
| | | |
| | | // 根据协议最大数据长度调整缓冲区 |
| | | buf := make([]byte, 1024) |
| | |
| | | if buf[0] != 0x5A { |
| | | continue // 忽略错误帧 |
| | | } |
| | | fmt.Printf("Recive message %x\n", buf[:n]) // 打印协议控制字进行调试 |
| | | fmt.Printf("Recive message %X\n", buf[:n]) // 打印协议控制字进行调试 |
| | | |
| | | // 校验CRC |
| | | //fmt.Printf("Crc %x\n",buf[n-2 : n]) |
| | |
| | | |
| | | // 回调传送epc数据 |
| | | //callBack(buf[9 : 9+epcLength]) |
| | | epcData := fmt.Sprintf("%x", buf[9:9+epcLength]) |
| | | epcData := fmt.Sprintf("%X", buf[9:9+epcLength]) |
| | | if epcData == target { |
| | | scanCount++ |
| | | if scanCount > minCount { |
| | |
| | | if err != nil { |
| | | return err |
| | | } |
| | | |
| | | go func() { |
| | | for { |
| | | err := r.SendAck() |
| | | if err != nil { |
| | | fmt.Println("send ack.", err.Error()) |
| | | } |
| | | time.Sleep(30 * time.Second) |
| | | |
| | | if time.Now().Unix()-r.HeartbeatTime > 120 { |
| | | r.Alive = false |
| | | } |
| | | } |
| | | }() |
| | | |
| | | // 根据协议最大数据长度调整缓冲区 |
| | | buf := make([]byte, 1024) |
| | |
| | | if buf[0] != 0x5A { |
| | | continue // 忽略错误帧 |
| | | } |
| | | fmt.Printf("Read message %x\n", buf[:n]) // 打印协议控制字进行调试 |
| | | |
| | | fmt.Printf("Read message %X\n", buf[:n]) // 打印协议控制字进行调试 |
| | | |
| | | // 校验CRC |
| | | //fmt.Printf("Crc %x\n",buf[n-2 : n]) |
| | | receivedCrc := binary.BigEndian.Uint16(buf[n-2 : n]) |
| | | computedCrc := CRC16XMODEM(buf[1 : n-2]) |
| | | if receivedCrc != (computedCrc & 0xFFFF) { |
| | |
| | | continue |
| | | } |
| | | |
| | | // 解析协议控制字 (仅在需要时使用) |
| | | //fmt.Printf("Control Word: %x\n", buf[1:5]) // 打印协议控制字进行调试 |
| | | r.HeartbeatTime = time.Now().Unix() |
| | | r.Alive = true |
| | | |
| | | // 解析协议控制字 |
| | | controlWord := binary.BigEndian.Uint32(buf[1:5]) |
| | | if controlWord != ControlWordEPCReadResponse6C { |
| | | fmt.Printf("Control Word: %d, rec word %d\n", ControlWordEPCReadResponse6C, controlWord) |
| | | continue |
| | | |
| | | switch controlWord { |
| | | case ControlWordDeviceInfo: |
| | | parseDeviceInfo(buf) |
| | | case ControlWordEPCReadResponse6C: |
| | | // 解析EPC数据长度 |
| | | epcLength := binary.BigEndian.Uint16(buf[7:9]) |
| | | |
| | | r.EPCData = fmt.Sprintf("%X", buf[9:9+epcLength]) |
| | | |
| | | // 回调传送epc数据 |
| | | //callBack(buf[9 : 9+epcLength]) |
| | | |
| | | fmt.Printf("Read epc %s\n", r.EPCData) |
| | | } |
| | | |
| | | // 解析EPC数据长度 |
| | | epcLength := binary.BigEndian.Uint16(buf[7:9]) |
| | | //fmt.Printf("EPC length %d, EPC %x \n", epcLength, buf[9:9+epcLength]) |
| | | |
| | | // 回调传送epc数据 |
| | | //callBack(buf[9 : 9+epcLength]) |
| | | r.EPCData = fmt.Sprintf("%X", buf[9:9+epcLength]) |
| | | r.ReadTimestamp = time.Now().Unix() |
| | | |
| | | fmt.Printf("read epc %s\n", r.EPCData) |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | _, err := r.DevPort.Write(data) |
| | | if err != nil { |
| | | return err |
| | | } |
| | | |
| | | // todo parse response |
| | | r.ReadResponse() |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func (r *Reader) SendAck() error { |
| | | cmd := "5A000101000000DCE5" |
| | | //cmd := "5A000111120004000000015FFB" |
| | | data, _ := hex.DecodeString(cmd) |
| | | |
| | | _, err := r.DevPort.Write(data) |
| | | return err |
| | | } |
| | | |
| | | func (r *Reader) SetPower(val int) error { |
| | | if val < 10 || val > 33 { |
| | | return fmt.Errorf("value out of range, must be between 0 and 255") |
| | | } |
| | | |
| | | frame := []byte{0x5A, 0x00, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01} |
| | | frame = append(frame, byte(val)) |
| | | |
| | | crc := CRC16XMODEM(frame[1:]) |
| | | crcBytes := make([]byte, 2) |
| | | binary.BigEndian.PutUint16(crcBytes, crc) |
| | | frame = append(frame, crcBytes...) |
| | | |
| | | fmt.Printf("set power %d, cmd %2X\n", val, frame) |
| | | |
| | | _, err := r.DevPort.Write(frame) |
| | | if err != nil { |
| | | return nil |
| | | } |
| | | |
| | |
| | | return err |
| | | } |
| | | |
| | | func (r *Reader) SetPower20() error { |
| | | cmd := "5A0001020100020132D230" |
| | | |
| | | func (r *Reader) SetBuzzer(enable bool) error { |
| | | cmd := "5A0001011E000100653D" |
| | | if !enable { |
| | | cmd = "5A0001011E000101751C" |
| | | } |
| | | data, _ := hex.DecodeString(cmd) |
| | | |
| | | _, err := r.DevPort.Write(data) |
| | | if err != nil { |
| | | return nil |
| | | return err |
| | | } |
| | | |
| | | // todo parse response |
| | | r.ReadResponse() |
| | | |
| | | return err |
| | | return nil |
| | | } |
| | | |
| | | func parseDeviceInfo(bytes []byte) { |
| | | // 初始化解析的偏移量 |
| | | offset := 1 |
| | | |
| | | // 解析协议控制字 |
| | | controlWord := bytes[offset : offset+4] |
| | | offset += 4 |
| | | |
| | | // 解析数据长度 |
| | | dataLength := int(bytes[offset])*256 + int(bytes[offset+1]) |
| | | offset += 2 |
| | | |
| | | // 解析读写器序列号 |
| | | serialNumberLen := int(bytes[offset])*256 + int(bytes[offset+1]) |
| | | offset += 2 |
| | | serialNumber := bytes[offset : offset+serialNumberLen] |
| | | offset += serialNumberLen |
| | | |
| | | // 解析上电时间(4字节) |
| | | upTimeBytes := bytes[offset : offset+4] |
| | | offset += 4 |
| | | |
| | | // 解析基带编译时间长度(2字节) |
| | | baseBandTimeLen := int(bytes[offset])*256 + int(bytes[offset+1]) |
| | | offset += 2 |
| | | |
| | | // 解析基带编译时间(根据长度字段解析) |
| | | baseBandTime := bytes[offset : offset+baseBandTimeLen] |
| | | offset += baseBandTimeLen |
| | | |
| | | // 解析应用软件版本(4字节) |
| | | appVer := bytes[offset : offset+4] |
| | | offset += 4 |
| | | |
| | | // 将字节数组转换为可读字符串 |
| | | serialNumberStr := string(serialNumber) |
| | | baseBandTimeStr := string(baseBandTime) |
| | | appVerStr := fmt.Sprintf("%d.%d.%d.%d", appVer[0], appVer[1], appVer[2], appVer[3]) |
| | | |
| | | // 解析上电时间(将字节数组转为整数) |
| | | upTime := int(upTimeBytes[0])<<24 + int(upTimeBytes[1])<<16 + int(upTimeBytes[2])<<8 + int(upTimeBytes[3]) |
| | | |
| | | // 打印结果 |
| | | fmt.Printf("协议控制字: %X\n", controlWord) |
| | | fmt.Printf("数据长度: %d\n", dataLength) |
| | | fmt.Printf("读写器序列号: %s\n", serialNumberStr) |
| | | fmt.Printf("读写器上电时间: %d秒\n", upTime) |
| | | fmt.Printf("基带编译时间: %s\n", baseBandTimeStr) |
| | | fmt.Printf("应用软件版本: %s\n", appVerStr) |
| | | } |