gate.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package parking
  2. import (
  3. "errors"
  4. "fmt"
  5. "sync"
  6. "time"
  7. "go.uber.org/zap"
  8. "wails-app/internal/dao"
  9. "wails-app/internal/global"
  10. )
  11. const defaultGateValidTime byte = 2
  12. // GateController 统一道闸设备控制接口,具体协议由设备模块实现。
  13. type GateController interface {
  14. OpenGate(deviceCode string, validTime byte) error
  15. CloseGate(deviceCode string, validTime byte) error
  16. IsGateConnected(deviceCode string) bool
  17. }
  18. // GateDeviceOption 是工作台可见的安全设备信息,不包含串口、IP 等连接参数。
  19. type GateDeviceOption struct {
  20. DeviceCode string `json:"device_code"`
  21. DeviceName string `json:"device_name"`
  22. ChannelID uint `json:"channel_id"`
  23. ChannelCode string `json:"channel_code"`
  24. ChannelName string `json:"channel_name"`
  25. Direction string `json:"direction"`
  26. ParkingLotID uint `json:"parking_lot_id"`
  27. Connected bool `json:"connected"`
  28. Simulated bool `json:"simulated"`
  29. }
  30. // GateRuntimeStatus 表示统一控制器中的设备实时状态。
  31. type GateRuntimeStatus struct {
  32. Connected bool
  33. Simulated bool
  34. }
  35. // SimulatedGateController 用于无硬件环境联调,只记录指令,不访问串口或网络设备。
  36. type SimulatedGateController struct {
  37. mu sync.RWMutex
  38. lastAction map[string]string
  39. }
  40. // NewSimulatedGateController 创建模拟道闸控制器。
  41. func NewSimulatedGateController() *SimulatedGateController {
  42. return &SimulatedGateController{lastAction: make(map[string]string)}
  43. }
  44. func (c *SimulatedGateController) record(deviceCode, action string) error {
  45. if deviceCode == "" {
  46. return errors.New("道闸设备编码不能为空")
  47. }
  48. c.mu.Lock()
  49. c.lastAction[deviceCode] = action
  50. c.mu.Unlock()
  51. if global.GVA_LOG != nil {
  52. global.GVA_LOG.Warn("模拟道闸指令", zap.String("device_code", deviceCode), zap.String("action", action), zap.Time("time", time.Now()))
  53. }
  54. return nil
  55. }
  56. // OpenGate 模拟开闸。
  57. func (c *SimulatedGateController) OpenGate(deviceCode string, _ byte) error {
  58. return c.record(deviceCode, "open")
  59. }
  60. // CloseGate 模拟关闸。
  61. func (c *SimulatedGateController) CloseGate(deviceCode string, _ byte) error {
  62. return c.record(deviceCode, "close")
  63. }
  64. // IsGateConnected 在模拟模式下将所有有效设备编码视为在线。
  65. func (c *SimulatedGateController) IsGateConnected(deviceCode string) bool {
  66. return deviceCode != ""
  67. }
  68. func (c *SimulatedGateController) isSimulator() bool {
  69. return true
  70. }
  71. var (
  72. gateControllerMu sync.RWMutex
  73. gateController GateController
  74. )
  75. // SetGateController 注入设备控制实现,避免停车业务反向依赖具体设备协议。
  76. func SetGateController(controller GateController) {
  77. gateControllerMu.Lock()
  78. defer gateControllerMu.Unlock()
  79. gateController = controller
  80. }
  81. func currentGateController() GateController {
  82. gateControllerMu.RLock()
  83. defer gateControllerMu.RUnlock()
  84. return gateController
  85. }
  86. // GetGateRuntimeStatus 返回设备在当前控制器中的实时连接状态。
  87. func GetGateRuntimeStatus(deviceCode string) GateRuntimeStatus {
  88. controller := currentGateController()
  89. if controller == nil || deviceCode == "" {
  90. return GateRuntimeStatus{}
  91. }
  92. status := GateRuntimeStatus{Connected: controller.IsGateConnected(deviceCode)}
  93. if simulator, ok := controller.(interface{ isSimulator() bool }); ok {
  94. status.Simulated = simulator.isSimulator()
  95. }
  96. return status
  97. }
  98. // OpenGateByDeviceCode 通过统一控制器开闸。
  99. func (s *PassageService) OpenGateByDeviceCode(deviceCode string, validTime byte) error {
  100. if deviceCode == "" {
  101. return errors.New("道闸设备编码不能为空")
  102. }
  103. controller := currentGateController()
  104. if controller == nil {
  105. return errors.New("道闸控制器未初始化")
  106. }
  107. if validTime == 0 {
  108. validTime = defaultGateValidTime
  109. }
  110. return controller.OpenGate(deviceCode, validTime)
  111. }
  112. // CloseGateByDeviceCode 通过统一控制器关闸。
  113. func (s *PassageService) CloseGateByDeviceCode(deviceCode string, validTime byte) error {
  114. if deviceCode == "" {
  115. return errors.New("道闸设备编码不能为空")
  116. }
  117. controller := currentGateController()
  118. if controller == nil {
  119. return errors.New("道闸控制器未初始化")
  120. }
  121. if validTime == 0 {
  122. validTime = defaultGateValidTime
  123. }
  124. return controller.CloseGate(deviceCode, validTime)
  125. }
  126. // ListGateDevices 返回已启用且已绑定通道的道闸设备。
  127. func (s *PassageService) ListGateDevices() ([]GateDeviceOption, error) {
  128. if global.GVA_DB == nil {
  129. return nil, errors.New("数据库未初始化")
  130. }
  131. var readers []dao.UHFReader
  132. if err := global.GVA_DB.Preload("Channel").
  133. Where("is_active = ? AND channel_id > 0", true).
  134. Order("device_name ASC").Find(&readers).Error; err != nil {
  135. return nil, err
  136. }
  137. options := make([]GateDeviceOption, 0, len(readers))
  138. for _, reader := range readers {
  139. if reader.Channel == nil {
  140. continue
  141. }
  142. parkingLotID := reader.Channel.ParkingLotID
  143. if parkingLotID == 0 {
  144. parkingLotID = reader.ParkingLotID
  145. }
  146. runtimeStatus := GetGateRuntimeStatus(reader.DeviceCode)
  147. options = append(options, GateDeviceOption{
  148. DeviceCode: reader.DeviceCode, DeviceName: reader.DeviceName,
  149. ChannelID: reader.ChannelID, ChannelCode: reader.Channel.ChannelCode,
  150. ChannelName: reader.Channel.ChannelName, Direction: reader.Channel.Direction,
  151. ParkingLotID: parkingLotID,
  152. Connected: runtimeStatus.Connected,
  153. Simulated: runtimeStatus.Simulated,
  154. })
  155. }
  156. return options, nil
  157. }
  158. func gateOperationError(action, deviceCode string, err error) error {
  159. return fmt.Errorf("道闸%s失败(设备 %s): %w", action, deviceCode, err)
  160. }