package lc import ( "context" "github.com/sirupsen/logrus" "lc-smartX/util" "lc-smartX/util/gopool" "time" // 仅引入Redis客户端核心依赖 "github.com/redis/go-redis/v9" ) // Redis基础配置(极简版,仅用于初始化连接) const ( RedisInitKey = "sentinel:init:flag" // 仅用于标记Redis初始化完成的空Key RedisKeyPrefix = "plate_results" ) // 单个哨兵服务(仅新增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{} // 停止信号通道 } // 启动哨兵服务 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() // 原有逻辑:初始化设备 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() // 原有逻辑:启动主循环 s.Serve() } // === 核心:极简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 } // 3. 写入初始化标记Key(仅标记,无业务数据) if err := s.rdb.Set(s.ctx, RedisInitKey, "initialized", 0).Err(); err != nil { logrus.Warn("⚠️ Redis初始化标记写入失败", "err", err) } else { logrus.Info("✅ Redis初始化完成(仅连接+标记)") } } // 原有逻辑:初始化本哨兵的设备(屏幕+喇叭) 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, } } // 原有逻辑:实现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) } } }