config/config.go | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
go.mod | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
go.sum | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
rfid/crc.go | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
rfid/proto.go | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
rfid/rw.go | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
service/nvcs.go | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
service/rfid.go | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
config/config.go
@@ -60,11 +60,19 @@ // 梯控设备 type nvcs struct { Mac string `mapstructure:"mac"` Model string `mapstructure:"model"` // 型号 Port string `mapstructure:"port"` // 端口 OSD string `mapstructure:"osd"` RunState bool `mapstructure:"run-state"` WaitRunTime int `mapstructure:"wait-run-time"` } type rfid struct { DevName string `mapstructure:"dev"` Baud int `mapstructure:"baud"` EPC string `mapstructure:"epc"` Position uint8 `mapstructure:"postion"` } type rateLimit struct { @@ -85,6 +93,7 @@ var NVCSConf = &nvcs{} var ImageConf = &image{} var SysTimeConf = &sysTime{} var RFIDConf = &rfid{} // Init is an exported method that takes the environment starts the viper // (external lib) and returns the configuration struct. @@ -115,6 +124,7 @@ v.UnmarshalKey("rate-limit", RateLimitConf) v.UnmarshalKey("client", ClientConf) v.UnmarshalKey("nvcs", NVCSConf) v.UnmarshalKey("rfid", RFIDConf) v.UnmarshalKey("image", ImageConf) if LogConf.Level == "" { go.mod
@@ -48,6 +48,7 @@ github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect go.uber.org/atomic v1.9.0 // indirect go.sum
@@ -113,6 +113,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= rfid/crc.go
New file @@ -0,0 +1,18 @@ package rfid func CRC16XMODEM(data []byte) uint16 { var crc uint16 = 0x0000 // 初始化 CRC 值 polynomial := uint16(0x1021) for _, b := range data { crc ^= uint16(b) << 8 for i := 0; i < 8; i++ { if crc&0x8000 != 0 { crc = (crc << 1) ^ polynomial } else { crc <<= 1 } } } return crc } rfid/proto.go
New file @@ -0,0 +1,5 @@ package rfid const ( ControlWordEPCReadResponse6C uint32 = 0x00011200 ) rfid/rw.go
New file @@ -0,0 +1,154 @@ package rfid import ( "encoding/binary" "encoding/hex" "fmt" "time" "github.com/tarm/serial" ) func NewReader(devName string, baud, readDuration int) *Reader { return &Reader{ DevName: devName, Baud: baud, ReadDuration: readDuration, } } type Reader struct { DevName string Baud int ReadDuration int DevPort *serial.Port } func (r *Reader) OpenSerial() (err error) { config := &serial.Config{ Name: r.DevName, Baud: r.Baud, ReadTimeout: 2 * time.Second, } r.DevPort, err = serial.OpenPort(config) return err } func (r *Reader) CloseSerial() error { return r.DevPort.Close() } // AutoReadStart 开启自动读取, 返回写入的指令长度和错误 func (r *Reader) StartAutoRead() error { cmd := "5A0001010B00100001000B0001021000050000000101005EE2" data, _ := hex.DecodeString(cmd) _, err := r.DevPort.Write(data) if err != nil { return nil } // todo parse response r.ReadResponse() return err } // StopAutoRead 停止读取 func (r *Reader) StopAutoRead() error { cmd := "5A000102FF0000885A" data, _ := hex.DecodeString(cmd) _, err := r.DevPort.Write(data) if err != nil { return nil } // todo parse response r.ReadResponse() return err } func (r *Reader) ReadResponse() (int, error) { buf := make([]byte, 1024) // 根据协议最大数据长度调整缓冲区 return r.DevPort.Read(buf) } func (r *Reader) ScanSpecificEPC(target string, minCount int) (bool, error) { err := r.StartAutoRead() if err != nil { return false, err } defer r.StopAutoRead() stop := time.After(time.Duration(r.ReadDuration) * time.Second) // 根据协议最大数据长度调整缓冲区 buf := make([]byte, 1024) scanCount := 0 for { select { case <-stop: fmt.Println("读取已超时") return false, nil default: for i := 0; i < 1024; i++ { buf[i] = 0 // 清零或其他处理 } n, err := r.DevPort.Read(buf) if err != nil { return false, err } if n == 0 || n < 8 { continue // 如果没有读取到数据 } // 检查帧头 if buf[0] != 0x5A { continue // 忽略错误帧 } fmt.Printf("Recive 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) { fmt.Println("CRC check failed") continue } // 解析协议控制字 (仅在需要时使用) //fmt.Printf("Control Word: %x\n", buf[1:5]) // 打印协议控制字进行调试 controlWord := binary.BigEndian.Uint32(buf[1:5]) if controlWord != ControlWordEPCReadResponse6C { fmt.Printf("Control Word: %d, rec word %d\n", ControlWordEPCReadResponse6C, controlWord) continue } // 解析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]) epcData := fmt.Sprintf("%x", buf[9:9+epcLength]) if epcData == target { scanCount++ if scanCount > minCount { return true, nil } } fmt.Printf("read epc %s, target epc: %s", epcData, target) } } } service/nvcs.go
@@ -2,6 +2,8 @@ import ( "bytes" "encoding/binary" "encoding/hex" "encoding/json" "fmt" "io/ioutil" @@ -13,14 +15,39 @@ "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"` @@ -28,15 +55,15 @@ 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 ( @@ -44,6 +71,97 @@ RunUp RunDown ) var ElevatorRunState int 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.Debug("The floor has been calibrated.") } else { logger.Warn(err.Error()) } } } 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() { @@ -96,36 +214,38 @@ continue } var runState string var iRunSate int elevator := data.Elevator[0] // 记录电梯运行状态 iRunSate = data.Elevator[0].Status.RunDir ElevatorRunState = elevator.Status.RunDir var runStateStr string if config.NVCSConf.RunState { if data.Elevator[0].Status.RunDir == RunUp { runState = "上" } else if data.Elevator[0].Status.RunDir == RunDown { runState = "下" if elevator.Status.RunDir == RunUp { runStateStr = "上" } else if elevator.Status.RunDir == RunDown { runStateStr = "下" } } if !config.NVCSConf.RunState { runState = "" runStateStr = "" } // 设置osd 格式 "1F上 固 枪" if config.NVCSConf.OSD != "" { floorText := fmt.Sprintf("%s%s %s", data.Elevator[0].Status.FloorName, runState, config.NVCSConf.OSD) floorText := fmt.Sprintf("%s%s %s", data.Elevator[0].Status.FloorName, runStateStr, config.NVCSConf.OSD) // 调用hik api 将文字添加到osd的左下角 AddFloorToOSD(floorText) } if data.Elevator[0].Status.RunDir > 0 { continue // correct floor when elevator stopped. if ElevatorRunState == 0 { A1CorrectFloor() } else { RunningCorrectTaskId = "" } elevator := data.Elevator[0] // 程序部署在设备端, 字符叠加器上报的名称允许为空. 在云端, 名称必须与摄像机相同 if elevator.Name == "" { @@ -135,7 +255,7 @@ var d = models.Positions{ DeviceId: elevator.Name, Pos: elevator.Status.FloorName, RunDir: iRunSate, RunDir: elevator.Status.RunDir, CreateTime: time.Now().Unix(), TimeString: time.Now().Format("2006-01-02 15:04:05"), } service/rfid.go
New file @@ -0,0 +1,5 @@ package service type RFIDReader struct { TagDetected bool // 是否检测到RFID标签 }