You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
4.5 KiB
250 lines
4.5 KiB
package maincontrollerClient
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
|
|
"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 gerror.Wrap(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 gerror.New("port is already open")
|
|
}
|
|
|
|
port, err := serial.Open(sp.address, sp.config)
|
|
if err != nil {
|
|
return gerror.Wrap(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)
|
|
_, err := sp.port.Write(data)
|
|
if err != nil {
|
|
return nil, gerror.New("数据写入失败")
|
|
}
|
|
timer := time.NewTimer(time.Second * 3)
|
|
select {
|
|
case <-timer.C:
|
|
sp.SetisBIO(false)
|
|
return nil, gerror.New("数据读取超时")
|
|
case message := <-sp.bioChan:
|
|
sp.SetisBIO(false)
|
|
return message, nil
|
|
}
|
|
}
|
|
|
|
// Close 关闭串口连接
|
|
func (sp *SerialPort) Close() error {
|
|
|
|
if !sp.isOpen {
|
|
return gerror.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
|
|
}
|