From eff59c389fd046a75bb67b45e2e52dcc712413d9 Mon Sep 17 00:00:00 2001 From: zhangzengfei <zhangzengfei@smartai.com> Date: 星期五, 13 九月 2024 01:19:38 +0800 Subject: [PATCH] add rfid --- config/config.go | 10 + go.sum | 2 rfid/rw.go | 154 +++++++++++++++++++++++++ rfid/proto.go | 5 rfid/crc.go | 18 +++ go.mod | 1 service/nvcs.go | 162 +++++++++++++++++++++++--- service/rfid.go | 5 8 files changed, 336 insertions(+), 21 deletions(-) diff --git a/config/config.go b/config/config.go index f4609ee..31ebd4d 100644 --- a/config/config.go +++ b/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 == "" { diff --git a/go.mod b/go.mod index dc122a9..5e9b101 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 03cfcb9..5e868cd 100644 --- a/go.sum +++ b/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= diff --git a/rfid/crc.go b/rfid/crc.go new file mode 100644 index 0000000..ec15d99 --- /dev/null +++ b/rfid/crc.go @@ -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 +} diff --git a/rfid/proto.go b/rfid/proto.go new file mode 100644 index 0000000..cbd59f8 --- /dev/null +++ b/rfid/proto.go @@ -0,0 +1,5 @@ +package rfid + +const ( + ControlWordEPCReadResponse6C uint32 = 0x00011200 +) diff --git a/rfid/rw.go b/rfid/rw.go new file mode 100644 index 0000000..fd2fc7b --- /dev/null +++ b/rfid/rw.go @@ -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 + } + + // 瑙f瀽鍗忚鎺у埗瀛� (浠呭湪闇�瑕佹椂浣跨敤) + //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 + } + + // 瑙f瀽EPC鏁版嵁闀垮害 + epcLength := binary.BigEndian.Uint16(buf[7:9]) + //fmt.Printf("EPC length %d, EPC %x \n", epcLength, buf[9:9+epcLength]) + + // 鍥炶皟浼犻�乪pc鏁版嵁 + //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) + } + } +} diff --git a/service/nvcs.go b/service/nvcs.go index d5b1f41..3e21a84 100644 --- a/service/nvcs.go +++ b/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"), } diff --git a/service/rfid.go b/service/rfid.go new file mode 100644 index 0000000..4c546fc --- /dev/null +++ b/service/rfid.go @@ -0,0 +1,5 @@ +package service + +type RFIDReader struct { + TagDetected bool // 鏄惁妫�娴嬪埌RFID鏍囩 +} -- Gitblit v1.8.0