package maincontrollerClient import ( "bytes" "context" "encoding/binary" "encoding/json" "github.com/towgo/towgo/errors/terror" "io" "reflect" "sync" "time" "log" "go.bug.st/serial" ) var serialPortMode bool var serialPort *SerialPort // SerialPort 串口对象 type SerialPort struct { config *serial.Mode address string port serial.Port isOpen bool ctx context.Context cancel context.CancelFunc dataChan chan byte onData []func([]byte) ticket *time.Ticker isBIO bool bioChan chan []byte bioWriteLock sync.Mutex bioLocker sync.Mutex } // NewSerialPort 创建串口实例 func NewSerialPort(portName string, baudRate int) *SerialPort { return &SerialPort{ address: portName, config: &serial.Mode{ BaudRate: baudRate, }, } } func UseSerialPort(portName string, baudRate int) error { serialPortMode = true serialPort = &SerialPort{ address: portName, config: &serial.Mode{ BaudRate: baudRate, }, } err := serialPort.Open() if err != nil { return err } serialPort.OnDataReceived(func(data []byte) { t := time.NewTimer(time.Millisecond * 100) if serialPort.IsBIO() { <-t.C for { select { case <-serialPort.bioChan: continue default: serialPort.bioChan <- data } break } } else { log.Printf("无效上传的数据(仅支持BIO模式),丢弃:% X", (data)) } }) return nil } // Open 打开串口连接 func (sp *SerialPort) Open() error { if sp.isOpen { return terror.New("port is already open") } port, err := serial.Open(sp.address, sp.config) if err != nil { return err } sp.dataChan = make(chan byte, 1) sp.bioChan = make(chan []byte, 1) sp.port = port sp.ctx, sp.cancel = context.WithCancel(context.Background()) sp.isOpen = true go sp.readLoop() go sp.readChanLoop() return nil } func (sp *SerialPort) BIO(data []byte) ([]byte, error) { sp.bioWriteLock.Lock() defer sp.bioWriteLock.Unlock() sp.SetisBIO(true) sp.port.Write(data) timer := time.NewTimer(time.Second * 3) select { case <-timer.C: sp.SetisBIO(false) return nil, terror.New("数据读取超时") case message := <-sp.bioChan: sp.SetisBIO(false) return message, nil } } // Close 关闭串口连接 func (sp *SerialPort) Close() error { if !sp.isOpen { return terror.New("port is already closed") } // 执行关闭操作 err := sp.port.Close() sp.isOpen = false return err } // OnDataReceived 注册数据接收事件回调 func (sp *SerialPort) OnDataReceived(callback func([]byte)) { sp.onData = append(sp.onData, callback) } func (sp *SerialPort) readChanLoop() { sp.ticket = time.NewTicker(time.Millisecond * 100) var buf []byte for { if !sp.isOpen { return } select { case <-sp.ctx.Done(): return case <-sp.ticket.C: if len(buf) > 0 { callback := sp.onData for _, f := range callback { go f(buf) } buf = []byte{} } case b := <-sp.dataChan: buf = append(buf, b) } } } // 读取循环 func (sp *SerialPort) readLoop() { buf := make([]byte, 1) for { select { case <-sp.ctx.Done(): return default: _, err := sp.port.Read(buf) sp.ticket.Reset(time.Millisecond * 100) if err != nil { if err != io.EOF { if sp.isOpen { sp.port.Close() sp.isOpen = false } } return } sp.dataChan <- buf[0] } } } func ToBytes(data interface{}) ([]byte, error) { if data == nil { return nil, nil } // 处理 []byte 和字符串 switch v := data.(type) { case []byte: return v, nil case string: return []byte(v), nil } // 处理基本类型 val := reflect.ValueOf(data) for { if val.Kind() == reflect.Interface { val = val.Elem() continue } break } switch val.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: buf := new(bytes.Buffer) if err := binary.Write(buf, binary.BigEndian, data); err != nil { return nil, err } return buf.Bytes(), nil default: // 复杂类型使用JSON序列化 return json.Marshal(data) } } func (sp *SerialPort) SetisBIO(isBio bool) { sp.bioLocker.Lock() sp.isBIO = isBio sp.bioLocker.Unlock() } func (sp *SerialPort) IsBIO() bool { sp.bioLocker.Lock() defer sp.bioLocker.Unlock() return sp.isBIO }