Browse Source

部分bug修复

xu 2 months ago
parent
commit
8064612854

+ 1 - 1
server/config.yaml

@@ -125,7 +125,7 @@ mysql:
     config: charset=utf8mb4&parseTime=True&loc=Local
     db-name: lc_garage
     username: root
-    password: 123456
+    password: root
 
     path: 127.0.0.1
     engine: ""

+ 1 - 1
server/dao/vehicle.go

@@ -8,7 +8,7 @@ import (
 type Vehicle struct {
 	global.GVA_MODEL
 	PlateNumber   string       `gorm:"size:20;not null;uniqueIndex" json:"plate_number"` // 车牌号
-	RFIDTag       string       `gorm:"size:40;" json:"rfid_tag"`                         // 电子标签
+	RFIDTag       string       `gorm:"size:40;column:rfid_tag" json:"rfid_tag"`          // 电子标签
 	VehicleTypeID uint         `gorm:"not null" json:"vehicle_type_id"`                  // 车辆类型ID
 	VehicleType   *VehicleType `gorm:"foreignKey:VehicleTypeID" json:"vehicle_type"`     // 车辆类型关联
 	VehicleBrand  string       `gorm:"size:50" json:"vehicle_brand"`                     // 车辆品牌

+ 6 - 6
server/service/uhf/reader.go

@@ -124,7 +124,7 @@ func StartDeviceHandler(device *dao.UHFReader) error {
 		device:    device,
 		ctx:       ctx,
 		cancel:    cancel,
-		dataChan:  make(chan *ReportData, 100),
+		dataChan:  make(chan *ReportData, 200), // 适当加大
 		isRunning: true,
 	}
 
@@ -135,9 +135,10 @@ func StartDeviceHandler(device *dao.UHFReader) error {
 	go func() {
 		defer func() {
 			h.isRunning = false
-			r.Disconnect()
+			_ = r.Disconnect()
 			close(h.dataChan)
 			DeviceManager.Unregister(device.DeviceCode)
+			global.GVA_LOG.Info("设备已停止 code:" + device.DeviceCode)
 		}()
 
 		for {
@@ -147,9 +148,8 @@ func StartDeviceHandler(device *dao.UHFReader) error {
 			default:
 				buf, err := r.ReadData()
 				if err != nil {
-					// 读取出错,更新设备状态
 					global.GVA_DB.Model(device).Update("status", "offline")
-					// 自动重连
+					_ = r.Disconnect() // 先断开
 					time.Sleep(2 * time.Second)
 					if err := r.Connect(); err != nil {
 						continue
@@ -168,10 +168,11 @@ func StartDeviceHandler(device *dao.UHFReader) error {
 					continue
 				}
 
-				// 推送到数据通道
+				// 推送(不丢死,也不阻塞)
 				select {
 				case h.dataChan <- report:
 				default:
+					global.GVA_LOG.Warn("通道已满,丢弃数据 device" + device.DeviceCode)
 				}
 			}
 		}
@@ -192,7 +193,6 @@ func StartDeviceHandler(device *dao.UHFReader) error {
 		}
 	}()
 
-	// 更新设备状态
 	global.GVA_DB.Model(device).Update("status", "online")
 	return nil
 }

+ 37 - 42
server/service/uhf/serial_reader.go

@@ -2,7 +2,6 @@ package uhf
 
 import (
 	"errors"
-	"fmt"
 	"github.com/tarm/serial"
 	"io"
 	"os"
@@ -14,110 +13,106 @@ type SerialReader struct {
 	BaudRate    int
 	port        *serial.Port
 	isConnected bool
-	readBufSize int
+	buffer      []byte // 拼包缓冲区
 }
 
-// 初始化串口
 func NewSerialReader(comPort string, baudRate int) *SerialReader {
 	return &SerialReader{
-		COMPort:     comPort,
-		BaudRate:    baudRate,
-		readBufSize: 1024, // 足够容纳设备响应
+		COMPort:  comPort,
+		BaudRate: baudRate,
 	}
 }
 
-// Connect 建立串口连接(显式配置8数据位,和工具一致)
 func (s *SerialReader) Connect() error {
 	cfg := &serial.Config{
 		Name:        s.COMPort,
 		Baud:        s.BaudRate,
-		ReadTimeout: 1 * time.Second, // 读超时1秒,核心超时配置
-		Size:        8,               // 8位数据位(必须显式配置!)
+		ReadTimeout: 1 * time.Second,
+		Size:        8,
 		Parity:      serial.ParityNone,
 		StopBits:    serial.Stop1,
 	}
 	port, err := serial.OpenPort(cfg)
 	if err != nil {
 		s.isConnected = false
-		return fmt.Errorf("打开串口失败: %v", err)
+		return err
 	}
 	s.port = port
 	s.isConnected = true
 	return nil
 }
 
-// Disconnect 断开连接
 func (s *SerialReader) Disconnect() error {
 	if s.port != nil {
-		err := s.port.Close()
-		s.isConnected = false
-		return err
+		_ = s.port.Close()
 	}
+	s.isConnected = false
+	s.buffer = nil
 	return nil
 }
 
-// IsConnected 检查状态
 func (s *SerialReader) IsConnected() bool {
 	return s.isConnected
 }
 
-// SendData 发送串口数据(移除无效的Flush断言,保留核心发送逻辑)
 func (s *SerialReader) SendData(data []byte) error {
 	if !s.isConnected {
-		return errors.New("串口连接未建立")
+		return errors.New("串口连接")
 	}
-	// 完整写入所有字节
 	n, err := s.port.Write(data)
 	if err != nil {
-		return fmt.Errorf("发送数据失败: %v", err)
+		return err
 	}
-	// 校验是否发送完整(核心:确保指令全部发出)
 	if n != len(data) {
-		return fmt.Errorf("发送字节数不完整,发送%d/预期%d", n, len(data))
+		return errors.New("发送不完整")
 	}
-
-	// ✅ 移除无效的Flush断言(tarm/serial的Port无此方法)
-	// 注:tarm/serial的Write本身会立即写入串口,无需手动Flush
 	return nil
 }
 
-// ReadData 循环读取串口数据(无SetReadDeadline,依赖ReadTimeout)
+// ==============================
+// 🔥 关键修复:串口返回完整25字节帧
+// ==============================
 func (s *SerialReader) ReadData() ([]byte, error) {
 	if !s.isConnected {
-		return nil, errors.New("串口连接未建立")
+		return nil, errors.New("串口连接")
 	}
-	buf := make([]byte, s.readBufSize)
-	var resp []byte
 
-	// 循环读取,直到超时/拿到完整响应
+	buf := make([]byte, 1024)
 	for {
 		n, err := s.port.Read(buf)
 		if err != nil {
-			// 超时/EOF是正常结束(ReadTimeout触发)
 			if err == io.EOF || os.IsTimeout(err) {
 				break
 			}
 			s.isConnected = false
-			return nil, fmt.Errorf("读取数据失败: %v", err)
+			return nil, err
 		}
 		if n > 0 {
-			resp = append(resp, buf[:n]...)
-			// 检测到完整响应(帧头0xCF+最小长度7),提前退出
-			if len(resp) >= 7 && resp[0] == 0xCF {
-				break
+			s.buffer = append(s.buffer, buf[:n]...)
+		}
+
+		// 找25字节完整帧
+		for len(s.buffer) >= 25 {
+			if s.buffer[0] == 0xCF {
+				frame := s.buffer[:25]
+				s.buffer = s.buffer[25:]
+				return frame, nil
+			} else {
+				s.buffer = s.buffer[1:]
 			}
 		}
+
+		// 超时退出
+		if len(s.buffer) == 0 {
+			break
+		}
 	}
-	return resp, nil
+
+	return nil, errors.New("等待完整帧")
 }
 
-// SendAndRecv 串口发送并等待响应(增加发送后延迟,核心优化)
 func (s *SerialReader) SendAndRecv(data []byte) ([]byte, error) {
-	if err := s.SendData(data); err != nil {
-		return nil, err
-	}
-	// 发送后延迟100ms,给设备处理指令的时间(工具也有此隐性延迟)
+	_ = s.SendData(data)
 	time.Sleep(100 * time.Millisecond)
-	// 读取响应
 	return s.ReadData()
 }

+ 49 - 29
server/service/uhf/tcp_reader.go

@@ -11,39 +11,41 @@ type TCPReader struct {
 	Port        int
 	conn        net.Conn
 	isConnected bool
-	readBufSize int // 读取缓冲区大小
+	buffer      []byte // 内部拼包缓冲区(关键修复)
 }
 
+// 实现 Reader 接口的继电器方法(不再panic)
 func (t *TCPReader) CloseRelay1(validTime byte) error {
-	//TODO implement me
-	panic("implement me")
+	frame := buildRelayFrame(0x0077, 1, RELAY_OP_CLOSE, validTime)
+	_, err := t.SendAndRecv(frame)
+	return err
 }
 
 func (t *TCPReader) ReleaseRelay1() error {
-	//TODO implement me
-	panic("implement me")
+	frame := buildRelayFrame(0x0077, 1, RELAY_OP_RELEASE, 0)
+	_, err := t.SendAndRecv(frame)
+	return err
 }
 
 func (t *TCPReader) CloseRelay2(validTime byte) error {
-	//TODO implement me
-	panic("implement me")
+	frame := buildRelayFrame(0x0078, 2, RELAY_OP_CLOSE, validTime)
+	_, err := t.SendAndRecv(frame)
+	return err
 }
 
 func (t *TCPReader) ReleaseRelay2() error {
-	//TODO implement me
-	panic("implement me")
+	frame := buildRelayFrame(0x0078, 2, RELAY_OP_RELEASE, 0)
+	_, err := t.SendAndRecv(frame)
+	return err
 }
 
-// 初始化时设置默认缓冲区
 func NewTCPReader(ip string, port int) *TCPReader {
 	return &TCPReader{
-		IP:          ip,
-		Port:        port,
-		readBufSize: 1024, // 默认1K缓冲区
+		IP:   ip,
+		Port: port,
 	}
 }
 
-// Connect 建立TCP连接(原有逻辑不变)
 func (t *TCPReader) Connect() error {
 	addr := net.JoinHostPort(t.IP, string(rune(t.Port)))
 	conn, err := net.DialTimeout("tcp", addr, 5*time.Second)
@@ -56,51 +58,69 @@ func (t *TCPReader) Connect() error {
 	return nil
 }
 
-// Disconnect 断开连接(原有逻辑不变)
 func (t *TCPReader) Disconnect() error {
 	if t.conn != nil {
-		err := t.conn.Close()
-		t.isConnected = false
-		return err
+		_ = t.conn.Close()
 	}
+	t.isConnected = false
+	t.buffer = nil
 	return nil
 }
 
-// IsConnected 检查状态(原有逻辑不变)
 func (t *TCPReader) IsConnected() bool {
 	return t.isConnected
 }
 
-// SendData 仅发送数据(多设备批量发送用)
 func (t *TCPReader) SendData(data []byte) error {
 	if !t.isConnected {
-		return errors.New("TCP连接未建立")
+		return errors.New("TCP连接")
 	}
 	_, err := t.conn.Write(data)
+	if err != nil {
+		t.isConnected = false
+	}
 	return err
 }
 
-// ReadData 阻塞读取TCP数据(多设备监听用)
+// ==============================
+// 🔥 关键修复:TCP 自动拼包,返回完整25字节帧
+// ==============================
 func (t *TCPReader) ReadData() ([]byte, error) {
 	if !t.isConnected {
-		return nil, errors.New("TCP连接未建立")
+		return nil, errors.New("TCP连接")
 	}
-	buf := make([]byte, t.readBufSize)
+
+	// 读超时,防止永久阻塞
+	t.conn.SetReadDeadline(time.Now().Add(3 * time.Second))
+
+	buf := make([]byte, 1024)
 	n, err := t.conn.Read(buf)
 	if err != nil {
-		t.isConnected = false // 读失败标记为断开
+		t.isConnected = false
 		return nil, err
 	}
-	return buf[:n], nil
+
+	// 加入缓冲区
+	t.buffer = append(t.buffer, buf[:n]...)
+
+	// 找完整帧:0xCF 开头 + 25字节
+	for len(t.buffer) >= 25 {
+		if t.buffer[0] == 0xCF {
+			frame := t.buffer[:25]
+			t.buffer = t.buffer[25:]
+			return frame, nil
+		} else {
+			t.buffer = t.buffer[1:]
+		}
+	}
+
+	return nil, errors.New("等待完整帧")
 }
 
-// SendAndRecv 发送并等待响应(一问一答,多设备指令交互用)
 func (t *TCPReader) SendAndRecv(data []byte) ([]byte, error) {
 	if err := t.SendData(data); err != nil {
 		return nil, err
 	}
-	// 设置读超时,避免永久阻塞
 	t.conn.SetReadDeadline(time.Now().Add(3 * time.Second))
-	defer t.conn.SetReadDeadline(time.Time{}) // 恢复默认
 	return t.ReadData()
 }

+ 1 - 0
server/service/vehicle/vehicle.go

@@ -32,6 +32,7 @@ func (s *VehicleService) CreateVehicle(req request.VehicleCreate) error {
 	}
 	vehicle := dao.Vehicle{
 		PlateNumber:   req.PlateNumber,
+		RFIDTag:       req.RFIDTag,
 		VehicleTypeID: req.VehicleTypeID,
 		VehicleBrand:  req.VehicleBrand,
 		VehicleColor:  req.VehicleColor,

+ 1 - 1
web/.env.production

@@ -4,4 +4,4 @@ ENV = 'production'
 VITE_BASE_API = /api
 VITE_FILE_API = /api
 #下方修改为你的线上ip(如果需要在线使用表单构建工具时使用,其余情况无需使用以下环境变量)
-VITE_BASE_PATH = https://demo.gin-vue-admin.com
+VITE_BASE_PATH = http://127.0.0.1

+ 1 - 1
web/src/api/vehicle.js

@@ -3,7 +3,7 @@ import service from '@/utils/request'
 // 新增车主
 export const addVehicle = (data) => {
   return service({
-    url: '/owner/create',
+    url: '/vehicle/create',
     method: 'post',
     data
   })

+ 0 - 5
web/src/router/index.js

@@ -4,11 +4,6 @@ const routes = [{
   path: '/',
   redirect: '/login'
 },
-{
-  path: '/init',
-  name: 'Init',
-  component: () => import('@/view/init/index.vue')
-},
 {
   path: '/login',
   name: 'Login',