|
@@ -8,8 +8,9 @@ import (
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"wails-app/internal/dao"
|
|
"wails-app/internal/dao"
|
|
|
"wails-app/internal/global"
|
|
"wails-app/internal/global"
|
|
|
- "wails-app/internal/model/vehicle/request"
|
|
|
|
|
|
|
+ common "wails-app/internal/model/common"
|
|
|
"wails-app/internal/service"
|
|
"wails-app/internal/service"
|
|
|
|
|
+ "wails-app/internal/service/parking"
|
|
|
"sync"
|
|
"sync"
|
|
|
"time"
|
|
"time"
|
|
|
)
|
|
)
|
|
@@ -44,6 +45,11 @@ var DeviceManager = &Manager{
|
|
|
devices: make(map[string]*DeviceHandler),
|
|
devices: make(map[string]*DeviceHandler),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// init 注入道闸控制回调(避免循环依赖)
|
|
|
|
|
+func init() {
|
|
|
|
|
+ parking.GateOpener = DeviceManager.OpenGateByDeviceCode
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
type Manager struct {
|
|
type Manager struct {
|
|
|
mu sync.RWMutex
|
|
mu sync.RWMutex
|
|
|
devices map[string]*DeviceHandler
|
|
devices map[string]*DeviceHandler
|
|
@@ -81,6 +87,20 @@ func (m *Manager) Get(code string) (*DeviceHandler, bool) {
|
|
|
return h, ok
|
|
return h, ok
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// OpenGate 开闸(公开方法,供外部触发源调用,如摄像头识别、手动进出场)
|
|
|
|
|
+func (h *DeviceHandler) OpenGate(validTime byte) error {
|
|
|
|
|
+ return h.reader.CloseRelay1(validTime)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// OpenGateByDeviceCode 通过设备编码直接开闸(便捷方法)
|
|
|
|
|
+func (m *Manager) OpenGateByDeviceCode(deviceCode string, validTime byte) error {
|
|
|
|
|
+ h, ok := m.Get(deviceCode)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return fmt.Errorf("设备未连接: %s", deviceCode)
|
|
|
|
|
+ }
|
|
|
|
|
+ return h.OpenGate(validTime)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// ===================== 复用原有CRC和解析函数(无需修改)=====================
|
|
// ===================== 复用原有CRC和解析函数(无需修改)=====================
|
|
|
func uiCrc16Cal(pucY []byte, ucX uint8) uint16 {
|
|
func uiCrc16Cal(pucY []byte, ucX uint8) uint16 {
|
|
|
const PRESET_VALUE = 0xFFFF
|
|
const PRESET_VALUE = 0xFFFF
|
|
@@ -226,15 +246,7 @@ func parseReportData(deviceCode string, buf []byte) (*ReportData, error) {
|
|
|
}, nil
|
|
}, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 防抖与状态
|
|
|
|
|
-var (
|
|
|
|
|
- epcLastTime = make(map[string]int64)
|
|
|
|
|
- epcStatus = make(map[string]bool)
|
|
|
|
|
- epcMutex sync.Mutex
|
|
|
|
|
- debounceSecond = int64(3)
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-// handleReportData 处理UHF读取到的标签 + 完整权限判断
|
|
|
|
|
|
|
+// handleReportData 处理UHF读取到的标签(精简版:只做解析+防抖,业务逻辑委托给 PassageService)
|
|
|
func handleReportData(report *ReportData) {
|
|
func handleReportData(report *ReportData) {
|
|
|
if len(report.Epcs) == 0 {
|
|
if len(report.Epcs) == 0 {
|
|
|
return
|
|
return
|
|
@@ -242,113 +254,18 @@ func handleReportData(report *ReportData) {
|
|
|
|
|
|
|
|
epc := report.Epcs[0]
|
|
epc := report.Epcs[0]
|
|
|
fmt.Println("<UNK>", epc)
|
|
fmt.Println("<UNK>", epc)
|
|
|
- now := time.Now().Unix()
|
|
|
|
|
|
|
|
|
|
- // ===================== 【修复】锁只包防抖,绝不包业务!=====================
|
|
|
|
|
- epcMutex.Lock()
|
|
|
|
|
- // 3秒防抖
|
|
|
|
|
- if lastTime, ok := epcLastTime[epc]; ok && now-lastTime < debounceSecond {
|
|
|
|
|
- epcMutex.Unlock()
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- epcLastTime[epc] = now
|
|
|
|
|
- epcMutex.Unlock()
|
|
|
|
|
- // ===================== 锁已释放,后面逻辑不会阻塞 =====================
|
|
|
|
|
-
|
|
|
|
|
- // 获取设备处理器
|
|
|
|
|
- handler, exists := DeviceManager.Get(report.DeviceCode)
|
|
|
|
|
- if !exists {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // ===================== 查询设备 + 通道 =====================
|
|
|
|
|
- var device dao.UHFReader
|
|
|
|
|
- err := global.GVA_DB.Preload("Channel").First(&device, "device_code = ?", report.DeviceCode).Error
|
|
|
|
|
|
|
+ // 委托给统一的进出场服务(所有触发方式共用同一入口)
|
|
|
|
|
+ result, err := service.ServiceGroupApp.ParkingServiceGroup.PassageService.HandlePassage(common.PassageRequest{
|
|
|
|
|
+ RFIDTag: epc,
|
|
|
|
|
+ DeviceCode: report.DeviceCode,
|
|
|
|
|
+ })
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
|
|
+ fmt.Printf("⚠️ 进出场处理失败 | 设备:%s EPC:%s 错误:%v\n", report.DeviceCode, epc, err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- channel := device.Channel
|
|
|
|
|
- direction := channel.Direction
|
|
|
|
|
- allowTemporary := channel.AllowTemporary
|
|
|
|
|
-
|
|
|
|
|
- vehicleService := service.ServiceGroupApp.VehicleServiceGroup.VehicleService
|
|
|
|
|
- shortlistService := service.ServiceGroupApp.VehicleServiceGroup.ShortlistService
|
|
|
|
|
-
|
|
|
|
|
- // ===================== 1. 根据EPC查询车辆 =====================
|
|
|
|
|
- vehicle, err := vehicleService.GetVehicleByPlateNumber("", epc)
|
|
|
|
|
- isTempVehicle := false
|
|
|
|
|
- if err != nil || vehicle.ID == 0 {
|
|
|
|
|
- isTempVehicle = true
|
|
|
|
|
- } else {
|
|
|
|
|
- if vehicle.VehicleType != nil {
|
|
|
|
|
- isTempVehicle = (vehicle.VehicleTypeID == 1)
|
|
|
|
|
- } else {
|
|
|
|
|
- isTempVehicle = true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // ===================== 2. 查询黑白名单 =====================
|
|
|
|
|
- isBlack, _ := shortlistService.CheckVehicleShortlist(epc)
|
|
|
|
|
-
|
|
|
|
|
- // ===================== 3. 黑名单优先判断 =====================
|
|
|
|
|
- if isBlack {
|
|
|
|
|
- fmt.Printf("🚫 禁止通行 | 车辆在黑名单中 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // ===================== 4. 临时车规则 =====================
|
|
|
|
|
- // 临时车:只看通道是否允许临停
|
|
|
|
|
- if isTempVehicle && !allowTemporary {
|
|
|
|
|
- fmt.Printf("🚫 禁止通行 | 临时车不允许此通道 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // ===================== 5. 入口 =====================
|
|
|
|
|
- if direction == "in" {
|
|
|
|
|
- epcMutex.Lock()
|
|
|
|
|
- canIn := !epcStatus[epc]
|
|
|
|
|
- epcMutex.Unlock()
|
|
|
|
|
-
|
|
|
|
|
- if canIn {
|
|
|
|
|
- fmt.Printf("🟢 入口开闸 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
|
|
|
|
|
- handler.reader.CloseRelay1(2)
|
|
|
|
|
-
|
|
|
|
|
- var vehicleEntry request.VehicleEntry
|
|
|
|
|
- vehicleEntry.RFIDTag = epc
|
|
|
|
|
- vehicleEntry.ParkingLotID = channel.ParkingLotID
|
|
|
|
|
- _, err := vehicleService.VehicleEntry(vehicleEntry)
|
|
|
|
|
-
|
|
|
|
|
- if err == nil {
|
|
|
|
|
- epcMutex.Lock()
|
|
|
|
|
- epcStatus[epc] = true
|
|
|
|
|
- epcMutex.Unlock()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // ===================== 6. 出口 =====================
|
|
|
|
|
- if direction == "out" {
|
|
|
|
|
- fmt.Printf("🔴 出口开闸 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
|
|
|
|
|
- handler.reader.CloseRelay1(2)
|
|
|
|
|
-
|
|
|
|
|
- var vehicleExit request.VehicleExit
|
|
|
|
|
- vehicleExit.RFIDTag = epc
|
|
|
|
|
- _, err := vehicleService.VehicleExit(vehicleExit)
|
|
|
|
|
-
|
|
|
|
|
- if err == nil {
|
|
|
|
|
- epcMutex.Lock()
|
|
|
|
|
- epcStatus[epc] = false
|
|
|
|
|
- epcMutex.Unlock()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-// ===================== 手动重置离场 =====================
|
|
|
|
|
-func SetEpcExit(epc string) {
|
|
|
|
|
- epcMutex.Lock()
|
|
|
|
|
- defer epcMutex.Unlock()
|
|
|
|
|
- epcStatus[epc] = false
|
|
|
|
|
- fmt.Printf("🚙 手动重置离场 | EPC: %s\n", epc)
|
|
|
|
|
|
|
+ fmt.Printf("✅ 进出场成功 | 方向:%s 车牌:%s 费用:%.2f\n", result.Direction, result.PlateNumber, result.Fee)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ==============================
|
|
// ==============================
|