| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- package uhf
- import (
- "context"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "fmt"
- "server/dao"
- "server/global"
- "sync"
- "time"
- )
- // Reader 通用读写接口(串口和TCP都实现它)
- type Reader interface {
- Connect() error
- Disconnect() error
- SendData([]byte) error
- ReadData() ([]byte, error)
- IsConnected() bool
- }
- // 上报数据模型
- type ReportData struct {
- DeviceCode string `json:"device_code"` // 设备编码
- Hex string `json:"hex"` // 原始报文
- Epcs []string `json:"epcs"` // 解析出的EPC列表
- RSSI int `json:"rssi"` // 信号强度
- Antenna int `json:"antenna"` // 天线号
- Timestamp int64 `json:"timestamp"` // 上报时间
- }
- // 全局设备管理器(管理所有已连接的设备)
- var DeviceManager = &Manager{
- devices: make(map[string]*DeviceHandler),
- }
- type Manager struct {
- mu sync.RWMutex
- devices map[string]*DeviceHandler
- }
- // DeviceHandler 每个设备的处理实例(包含读写器和协程)
- type DeviceHandler struct {
- reader Reader
- device *dao.UHFReader
- ctx context.Context
- cancel context.CancelFunc
- dataChan chan *ReportData
- isRunning bool
- }
- // Register 注册设备
- func (m *Manager) Register(code string, h *DeviceHandler) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.devices[code] = h
- }
- // Unregister 注销设备
- func (m *Manager) Unregister(code string) {
- m.mu.Lock()
- defer m.mu.Unlock()
- delete(m.devices, code)
- }
- // Get 获取设备
- func (m *Manager) Get(code string) (*DeviceHandler, bool) {
- m.mu.RLock()
- defer m.mu.RUnlock()
- h, ok := m.devices[code]
- return h, ok
- }
- // ===================== 复用原有CRC和解析函数(无需修改)=====================
- func uiCrc16Cal(pucY []byte, ucX uint8) uint16 {
- const PRESET_VALUE = 0xFFFF
- const POLYNOMIAL = 0x8408
- var uiCrcValue uint16 = PRESET_VALUE
- for ucI := uint8(0); ucI < ucX; ucI++ {
- uiCrcValue = uiCrcValue ^ uint16(pucY[ucI])
- for ucJ := uint8(0); ucJ < 8; ucJ++ {
- if uiCrcValue&0x0001 != 0 {
- uiCrcValue = (uiCrcValue >> 1) ^ POLYNOMIAL
- } else {
- uiCrcValue = uiCrcValue >> 1
- }
- }
- }
- return (uiCrcValue << 8) | (uiCrcValue >> 8)
- }
- // StartDeviceHandler 启动单个设备的读写协程
- func StartDeviceHandler(device *dao.UHFReader) error {
- // 1. 根据连接类型创建读写器
- var r Reader
- switch device.ConnectType {
- case "serial":
- r = NewSerialReader(device.COMPort, device.BaudRate)
- case "tcp":
- r = NewTCPReader(device.IPAddress, device.Port)
- default:
- return fmt.Errorf("不支持的连接类型: %s", device.ConnectType)
- }
- // 2. 连接设备
- if err := r.Connect(); err != nil {
- return err
- }
- // 3. 创建上下文和handler
- ctx, cancel := context.WithCancel(context.Background())
- h := &DeviceHandler{
- reader: r,
- device: device,
- ctx: ctx,
- cancel: cancel,
- dataChan: make(chan *ReportData, 100),
- isRunning: true,
- }
- // 4. 注册到管理器
- DeviceManager.Register(device.DeviceCode, h)
- // 5. 启动读写协程
- go func() {
- defer func() {
- h.isRunning = false
- r.Disconnect()
- close(h.dataChan)
- DeviceManager.Unregister(device.DeviceCode)
- }()
- for {
- select {
- case <-ctx.Done():
- return
- default:
- buf, err := r.ReadData()
- if err != nil {
- // 读取出错,更新设备状态
- global.GVA_DB.Model(device).Update("status", "offline")
- // 自动重连
- time.Sleep(2 * time.Second)
- if err := r.Connect(); err != nil {
- continue
- }
- global.GVA_DB.Model(device).Update("status", "online")
- continue
- }
- if len(buf) == 0 {
- continue
- }
- // 解析上报数据
- report, err := parseReportData(device.DeviceCode, buf)
- if err != nil {
- continue
- }
- // 推送到数据通道
- select {
- case h.dataChan <- report:
- default:
- }
- }
- }
- }()
- // 6. 启动业务处理协程(你后续在这里写业务逻辑)
- go func() {
- for {
- select {
- case <-ctx.Done():
- return
- case report, ok := <-h.dataChan:
- if !ok {
- return
- }
- // 这里写你的业务逻辑,比如:
- // 1. 去重过滤标签
- // 2. 写入数据库
- // 3. 推送到前端websocket
- handleReportData(report)
- }
- }
- }()
- // 更新设备状态
- global.GVA_DB.Model(device).Update("status", "online")
- return nil
- }
- // parseReportData 解析上报数据(把你之前的逻辑搬过来)
- func parseReportData(deviceCode string, buf []byte) (*ReportData, error) {
- if len(buf) < 25 || buf[0] != 0xCF {
- return nil, errors.New("无效帧")
- }
- // CRC校验(复用你之前的函数)
- dataWithoutCRC := buf[:len(buf)-2]
- recvCRC := binary.LittleEndian.Uint16(buf[len(buf)-2:])
- calcCRC := uiCrc16Cal(dataWithoutCRC, uint8(len(dataWithoutCRC)))
- if recvCRC != calcCRC {
- return nil, errors.New("CRC校验失败")
- }
- // 解析字段
- rssi := int(buf[6])
- antenna := int(buf[22])
- epc := hex.EncodeToString(buf[9:21])
- return &ReportData{
- DeviceCode: deviceCode,
- Hex: hex.EncodeToString(buf),
- Epcs: []string{epc},
- RSSI: rssi,
- Antenna: antenna,
- Timestamp: time.Now().Unix(),
- }, nil
- }
- // handleReportData
- func handleReportData(report *ReportData) {
- // 示例:打印上报数据
- fmt.Printf("[业务处理] 设备:%s EPC:%s RSSI:%d 天线:%d\n",
- report.DeviceCode, report.Epcs[0], report.RSSI, report.Antenna)
- }
|