package lc import ( "context" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "lc-smartX/util" "lc-smartX/util/gopool" "log/slog" "time" // 仅引入Redis客户端核心依赖 "github.com/redis/go-redis/v9" ) // 单个哨兵服务(仅新增Redis客户端和上下文字段) type SentinelServer struct { RadarServer *RadarServer CameraServer *CameraServer Device IDevice // 本哨兵的设备(屏幕+喇叭) StateTicker *time.Ticker // 状态回滚定时器 ReTicker *time.Ticker // 重连定时器 // === 仅新增Redis核心字段 === rdb *redis.Client // Redis客户端 ctx context.Context // 上下文(用于Redis操作) fetchTicker *time.Ticker // 定时任务ticker stopChan chan struct{} // 停止信号通道 ginEngine *gin.Engine } // 启动哨兵服务 func StartSentinelServer() { s := &SentinelServer{ StateTicker: time.NewTicker(5 * time.Second), // 5秒后自动回滚 ReTicker: time.NewTicker(30 * time.Second), // 30秒重连检查 ctx: context.Background(), // 初始化Redis上下文 } // === 第一步:优先初始化Redis(极简版) === s.initRedis() deviceData, err := s.GetDeviceDataFromRedis() if err != nil { return } DeviceInitData = deviceData s.initGin() // 原有逻辑:初始化设备 s.initDevice() time.Sleep(2 * time.Second) // 原有逻辑:启动雷达服务 if util.Config.Server.SupportRadar { s.RadarServer = NewRadarEventServer() s.RadarServer.RegisterCallback(s) // 注册雷达回调 gopool.Go(s.RadarServer.Start) } // 原有逻辑:启动摄像头服务 if util.Config.Server.SupportCamera { s.CameraServer = NewCameraEventServer() s.CameraServer.RegisterCallback(s) // 注册摄像头回调 gopool.Go(s.CameraServer.Start) } // 启动定时任务:每3秒获取最新一条车牌号 s.StartFetchingLatestPlate() // 原有逻辑:启动主循环 go s.Serve() err = s.ginEngine.Run(":8080") if err != nil { return } } // === 核心:极简Redis初始化(仅连接+标记初始化) === func (s *SentinelServer) initRedis() { // 1. 创建Redis客户端 s.rdb = redis.NewClient(&redis.Options{ Addr: util.Config.RedisConfig.Addr, Password: util.Config.RedisConfig.Password, DB: util.Config.RedisConfig.DB, }) // 2. 测试Redis连接 if err := s.rdb.Ping(s.ctx).Err(); err != nil { logrus.Warn("⚠️ Redis连接失败,哨兵服务正常启动(无Redis支持)", "err", err) s.rdb = nil // 标记Redis不可用 return } } // 原有逻辑:初始化本哨兵的设备(屏幕+喇叭) func (s *SentinelServer) initDevice() { // 屏幕配置(取第一个屏幕) screenCfg := util.Config.Screens[0] screen := NewScreen(screenCfg.Name, screenCfg.Ip, screenCfg.Port) // 喇叭配置(取第一个喇叭) speakerCfg := util.Config.Speakers[0] speaker := NewIpCast( speakerCfg.Port, ) // 组合设备 s.Device = &SentinelDevice{ Info: DeviceInfo{ Name: screenCfg.Name, Ip: screenCfg.Ip, Port: screenCfg.Port, Audio: "支路来车", }, Screen: screen, Speaker: speaker, } } // ========== 修改Gin初始化:整合handler和路由 ========== func (s *SentinelServer) initGin() { // 1. 初始化Gin引擎 gin.SetMode(gin.DebugMode) s.ginEngine = gin.Default() // 2. 创建业务处理实例(注入哨兵服务依赖) handler := NewPlateDeviceHandler(s) // 3. 调用路由注册函数(路由与业务解耦) RegisterRoutes(s.ginEngine, handler) slog.Info("✅ Gin路由初始化成功") } // 原有逻辑:实现Notifier接口,处理雷达/摄像头事件 func (s *SentinelServer) Notify(text string, isProgram, speed int) { s.StateTicker.Reset(5 * time.Second) // 重置回滚定时器 go s.Device.Call(text, isProgram, speed) //gopool.Go(func() { // s.Device.Call(text, isProgram, speed) //}) // 触发设备警告 } // 原有逻辑:主循环:处理状态回滚和重连 func (s *SentinelServer) Serve() { for { select { case <-s.StateTicker.C: // 定时器触发,回滚设备状态 gopool.Go(s.Device.Rollback) case <-s.ReTicker.C: // 定时重连设备 gopool.Go(s.Device.Reconnect) } } }