server.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package lc
  2. import (
  3. "context"
  4. "github.com/gin-gonic/gin"
  5. "github.com/sirupsen/logrus"
  6. "lc-smartX/util"
  7. "lc-smartX/util/gopool"
  8. "log/slog"
  9. "time"
  10. // 仅引入Redis客户端核心依赖
  11. "github.com/redis/go-redis/v9"
  12. )
  13. // 单个哨兵服务(仅新增Redis客户端和上下文字段)
  14. type SentinelServer struct {
  15. RadarServer *RadarServer
  16. CameraServer *CameraServer
  17. Device IDevice // 本哨兵的设备(屏幕+喇叭)
  18. StateTicker *time.Ticker // 状态回滚定时器
  19. ReTicker *time.Ticker // 重连定时器
  20. // === 仅新增Redis核心字段 ===
  21. rdb *redis.Client // Redis客户端
  22. ctx context.Context // 上下文(用于Redis操作)
  23. fetchTicker *time.Ticker // 定时任务ticker
  24. stopChan chan struct{} // 停止信号通道
  25. ginEngine *gin.Engine
  26. }
  27. // 启动哨兵服务
  28. func StartSentinelServer() {
  29. s := &SentinelServer{
  30. StateTicker: time.NewTicker(5 * time.Second), // 5秒后自动回滚
  31. ReTicker: time.NewTicker(30 * time.Second), // 30秒重连检查
  32. ctx: context.Background(), // 初始化Redis上下文
  33. }
  34. // === 第一步:优先初始化Redis(极简版) ===
  35. s.initRedis()
  36. deviceData, err := s.GetDeviceDataFromRedis()
  37. if err != nil {
  38. return
  39. }
  40. DeviceInitData = deviceData
  41. s.initGin()
  42. // 原有逻辑:初始化设备
  43. s.initDevice()
  44. time.Sleep(2 * time.Second)
  45. // 原有逻辑:启动雷达服务
  46. if util.Config.Server.SupportRadar {
  47. s.RadarServer = NewRadarEventServer()
  48. s.RadarServer.RegisterCallback(s) // 注册雷达回调
  49. gopool.Go(s.RadarServer.Start)
  50. }
  51. // 原有逻辑:启动摄像头服务
  52. if util.Config.Server.SupportCamera {
  53. s.CameraServer = NewCameraEventServer()
  54. s.CameraServer.RegisterCallback(s) // 注册摄像头回调
  55. gopool.Go(s.CameraServer.Start)
  56. }
  57. // 启动定时任务:每3秒获取最新一条车牌号
  58. s.StartFetchingLatestPlate()
  59. // 原有逻辑:启动主循环
  60. go s.Serve()
  61. err = s.ginEngine.Run(":8080")
  62. if err != nil {
  63. return
  64. }
  65. }
  66. // === 核心:极简Redis初始化(仅连接+标记初始化) ===
  67. func (s *SentinelServer) initRedis() {
  68. // 1. 创建Redis客户端
  69. s.rdb = redis.NewClient(&redis.Options{
  70. Addr: util.Config.RedisConfig.Addr,
  71. Password: util.Config.RedisConfig.Password,
  72. DB: util.Config.RedisConfig.DB,
  73. })
  74. // 2. 测试Redis连接
  75. if err := s.rdb.Ping(s.ctx).Err(); err != nil {
  76. logrus.Warn("⚠️ Redis连接失败,哨兵服务正常启动(无Redis支持)", "err", err)
  77. s.rdb = nil // 标记Redis不可用
  78. return
  79. }
  80. }
  81. // 原有逻辑:初始化本哨兵的设备(屏幕+喇叭)
  82. func (s *SentinelServer) initDevice() {
  83. // 屏幕配置(取第一个屏幕)
  84. screenCfg := util.Config.Screens[0]
  85. screen := NewScreen(screenCfg.Name, screenCfg.Ip, screenCfg.Port)
  86. // 喇叭配置(取第一个喇叭)
  87. speakerCfg := util.Config.Speakers[0]
  88. speaker := NewIpCast(
  89. speakerCfg.Port,
  90. )
  91. // 组合设备
  92. s.Device = &SentinelDevice{
  93. Info: DeviceInfo{
  94. Name: screenCfg.Name,
  95. Ip: screenCfg.Ip,
  96. Port: screenCfg.Port,
  97. Audio: "支路来车",
  98. },
  99. Screen: screen,
  100. Speaker: speaker,
  101. }
  102. }
  103. // ========== 修改Gin初始化:整合handler和路由 ==========
  104. func (s *SentinelServer) initGin() {
  105. // 1. 初始化Gin引擎
  106. gin.SetMode(gin.DebugMode)
  107. s.ginEngine = gin.Default()
  108. // 2. 创建业务处理实例(注入哨兵服务依赖)
  109. handler := NewPlateDeviceHandler(s)
  110. // 3. 调用路由注册函数(路由与业务解耦)
  111. RegisterRoutes(s.ginEngine, handler)
  112. slog.Info("✅ Gin路由初始化成功")
  113. }
  114. // 原有逻辑:实现Notifier接口,处理雷达/摄像头事件
  115. func (s *SentinelServer) Notify(text string, isProgram, speed int) {
  116. s.StateTicker.Reset(5 * time.Second) // 重置回滚定时器
  117. go s.Device.Call(text, isProgram, speed)
  118. //gopool.Go(func() {
  119. // s.Device.Call(text, isProgram, speed)
  120. //}) // 触发设备警告
  121. }
  122. // 原有逻辑:主循环:处理状态回滚和重连
  123. func (s *SentinelServer) Serve() {
  124. for {
  125. select {
  126. case <-s.StateTicker.C:
  127. // 定时器触发,回滚设备状态
  128. gopool.Go(s.Device.Rollback)
  129. case <-s.ReTicker.C:
  130. // 定时重连设备
  131. gopool.Go(s.Device.Reconnect)
  132. }
  133. }
  134. }