server.go 4.0 KB

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