bizalarm.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package main
  2. import (
  3. "math"
  4. "runtime/debug"
  5. "strconv"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/sirupsen/logrus"
  10. "lc/common/models"
  11. "lc/common/util"
  12. )
  13. var _BizAlarmMgronce sync.Once
  14. var _BizAlarmMgrsingle *BizAlarmMgr
  15. func GetBizAlarmMgr() *BizAlarmMgr {
  16. _BizAlarmMgronce.Do(func() {
  17. _BizAlarmMgrsingle = &BizAlarmMgr{
  18. queue: util.NewQueue(10000),
  19. mapID: make(map[string]bool),
  20. mapIDLock: &sync.RWMutex{},
  21. mapAssociate: make(map[string][]Associate),
  22. mapStrategy: make(map[int]models.AlarmStrategy),
  23. mapPendingAlarm: make(map[string]*PendingAlarm),
  24. }
  25. })
  26. return _BizAlarmMgrsingle
  27. }
  28. type Associate struct {
  29. ID string
  30. Mid int
  31. Sid int
  32. Cid int
  33. }
  34. type BizValue struct {
  35. ID string
  36. Time time.Time
  37. Tid uint16
  38. Data map[uint16]float64 //sid->值
  39. }
  40. type PendingAlarm struct {
  41. Alarm *models.DeviceAlarm
  42. Duration uint8 //持续时间
  43. }
  44. type BizAlarmMgr struct {
  45. queue *util.MlQueue //待处理数据
  46. mapID map[string]bool //设备id->bool,用于判断数据是否要入队列
  47. mapIDLock *sync.RWMutex
  48. mapAssociate map[string][]Associate //设备id->关联信息
  49. mapStrategy map[int]models.AlarmStrategy //cid->告警策略
  50. mapPendingAlarm map[string]*PendingAlarm //key=设备id_sid_cid
  51. }
  52. func (o *BizAlarmMgr) PushData(value *BizValue) {
  53. o.mapIDLock.RLock()
  54. defer o.mapIDLock.RUnlock()
  55. if _, ok := o.mapID[value.ID]; ok {
  56. o.queue.Put(value)
  57. }
  58. }
  59. // Handler 定时更新告警策略
  60. func (o *BizAlarmMgr) Handler(args ...interface{}) interface{} {
  61. defer func() {
  62. if err := recover(); err != nil {
  63. time.Sleep(time.Second)
  64. gopool.Add(o.Handler, args)
  65. logrus.Errorf("BizAlarmMgr.Handler发生异常:%s", string(debug.Stack()))
  66. }
  67. }()
  68. o.loadConfig()
  69. o.LoadAlarm()
  70. timer := time.NewTicker(10 * time.Minute)
  71. timer2 := time.NewTicker(1 * time.Minute)
  72. for {
  73. select {
  74. case <-timer.C: //每隔10分钟更新告警配置
  75. o.loadConfig()
  76. case <-timer2.C: //每隔1分钟检查告警入库情况
  77. o.SaveAlarm()
  78. default:
  79. quantity := o.HandleOneData()
  80. if quantity == 0 {
  81. time.Sleep(10 * time.Millisecond)
  82. }
  83. }
  84. }
  85. return 0
  86. }
  87. func (o *BizAlarmMgr) HandleOneData() uint32 {
  88. bv, ok, quantity := o.queue.Get()
  89. if !ok {
  90. return 0
  91. }
  92. bizvalue, ok := bv.(*BizValue)
  93. if !ok {
  94. return quantity
  95. }
  96. //有配置,则处理
  97. var isAlarm uint8
  98. ass, ok := o.mapAssociate[bizvalue.ID]
  99. if !ok {
  100. return quantity
  101. }
  102. for _, v := range ass {
  103. if v.Mid != int(bizvalue.Tid) {
  104. continue
  105. }
  106. val, ok2 := bizvalue.Data[uint16(v.Sid)]
  107. if !ok2 {
  108. continue
  109. }
  110. c, ok3 := o.mapStrategy[v.Cid]
  111. if !ok3 {
  112. continue
  113. }
  114. val = Precision(val, 2, false)
  115. isAlarm = 0
  116. akey := bizvalue.ID + "_" + strconv.Itoa(v.Sid) + "_" + strconv.Itoa(v.Cid)
  117. if !IsEqual(float64(c.LowLimit), -9999.0) {
  118. if val < float64(c.LowLimit) { //告警状态,低于下限
  119. isAlarm = 1
  120. }
  121. }
  122. if !IsEqual(float64(c.UpLimit), -9999.0) && isAlarm == 0 {
  123. if val > float64(c.UpLimit) { //告警状态,超过上限
  124. isAlarm = 2
  125. }
  126. }
  127. aa, ok := o.mapPendingAlarm[akey]
  128. if isAlarm == 0 && ok { //之前有告警,当前数据表明告警结束
  129. if aa.Alarm.TStart.IsZero() || bizvalue.Time.Sub(aa.Alarm.TStart).Seconds() >= float64(aa.Duration) {
  130. aa.Alarm.EValue = float32(val)
  131. aa.Alarm.TEnd = bizvalue.Time
  132. o.mapPendingAlarm[akey] = aa
  133. } else {
  134. delete(o.mapPendingAlarm, akey)
  135. }
  136. } else if isAlarm > 0 && !ok { //之前无告警,当前数据表明告警开始
  137. var content string
  138. var limitvalue float64
  139. if isAlarm == 1 {
  140. content = "低于下限(" + strconv.FormatFloat(float64(c.LowLimit), 'f', 2, 64) + ")"
  141. limitvalue = float64(c.LowLimit)
  142. } else if isAlarm == 2 {
  143. content = "高于上限(" + strconv.FormatFloat(float64(c.UpLimit), 'f', 2, 64) + ")"
  144. limitvalue = float64(c.UpLimit)
  145. }
  146. aA := models.DeviceAlarm{
  147. DID: bizvalue.ID,
  148. TStart: bizvalue.Time,
  149. Threshold: float32(limitvalue),
  150. SValue: float32(val),
  151. Content: content,
  152. AlarmType: uint16(v.Cid),
  153. Level: 1,
  154. Sid: uint16(v.Sid),
  155. Cid: uint16(v.Cid),
  156. Cname: c.Name,
  157. }
  158. pa := PendingAlarm{
  159. Alarm: &aA,
  160. Duration: c.Duration,
  161. }
  162. o.mapPendingAlarm[akey] = &pa
  163. }
  164. }
  165. return quantity
  166. }
  167. func (o *BizAlarmMgr) SaveAlarm() {
  168. for k, v := range o.mapPendingAlarm {
  169. if v.Alarm.ID == 0 { //还未入过库
  170. if (v.Alarm.TEnd.IsZero() && util.MlNow().Sub(v.Alarm.TStart).Seconds() >= float64(v.Duration)) ||
  171. (!v.Alarm.TEnd.IsZero() && v.Alarm.TEnd.Sub(v.Alarm.TStart).Seconds() >= float64(v.Duration)) {
  172. err := models.G_db.Create(v.Alarm).Error
  173. if err != nil {
  174. logrus.Errorf("告警信息[%v]入库失败:%s", v.Alarm, err.Error())
  175. } else {
  176. if v.Alarm.TEnd.IsZero() {
  177. if err := redisCltRawData.HSet(DeviceAlarmId, k, v.Alarm.ID).Err(); err != nil {
  178. logrus.Errorf("设备[%s]告警数据[%d]缓存失败:%s", v.Alarm.DID, v.Alarm.ID, err.Error())
  179. }
  180. }
  181. }
  182. }
  183. } else { //已经入过库
  184. //判断是否已经结束,结束则更新告警结束时间、以及结束时的值
  185. if !v.Alarm.TEnd.IsZero() {
  186. err := v.Alarm.Update()
  187. if err != nil {
  188. logrus.Errorf("告警信息[%v]告警结束更新失败:%s", v.Alarm, err.Error())
  189. } else {
  190. delete(o.mapPendingAlarm, k)
  191. if err := redisCltRawData.HDel(DeviceAlarmId, k).Err(); err != nil {
  192. logrus.Errorf("设备[%s]告警数据[%d]缓存失败:%s", v.Alarm.DID, v.Alarm.ID, err.Error())
  193. }
  194. }
  195. }
  196. }
  197. }
  198. }
  199. func (o *BizAlarmMgr) LoadAlarm() {
  200. m, err := redisCltRawData.HGetAll(DeviceAlarmId).Result()
  201. if err != nil {
  202. logrus.Errorf("从redis缓存中加载业务告警信息失败:%s", err.Error())
  203. return
  204. }
  205. for k, v := range m {
  206. strlist := strings.Split(k, "_") //key规则:设备编码_sid_cid
  207. if len(strlist) != 3 {
  208. continue
  209. }
  210. cid, err := strconv.Atoi(strlist[2])
  211. if err != nil {
  212. continue
  213. }
  214. id, err2 := strconv.Atoi(v)
  215. if err2 != nil {
  216. continue
  217. }
  218. if as, ok := o.mapStrategy[cid]; ok {
  219. pa := PendingAlarm{
  220. Alarm: &models.DeviceAlarm{ID: int64(id)},
  221. Duration: as.Duration,
  222. }
  223. o.mapPendingAlarm[k] = &pa
  224. }
  225. }
  226. }
  227. func (o *BizAlarmMgr) loadConfig() {
  228. //重新读取策略
  229. arr, err := models.GetAllAlarmStrategy()
  230. if err == nil {
  231. o.mapStrategy = make(map[int]models.AlarmStrategy)
  232. for _, v := range arr {
  233. o.mapStrategy[int(v.ID)] = v
  234. }
  235. }
  236. //重新读取告警关联信息
  237. arr2, err2 := models.GetAllAlarmAssociate()
  238. if err2 == nil {
  239. o.mapAssociate = make(map[string][]Associate)
  240. mapID_ := make(map[string]bool)
  241. for _, v := range arr2 {
  242. x := Associate{ID: v.ID, Mid: v.Mid, Sid: v.Sid, Cid: v.Cid}
  243. o.mapAssociate[v.ID] = append(o.mapAssociate[v.ID], x)
  244. mapID_[v.ID] = true
  245. }
  246. o.mapIDLock.Lock()
  247. o.mapID = mapID_
  248. o.mapIDLock.Unlock()
  249. }
  250. }
  251. const MIN = 0.000001
  252. func IsEqual(f1, f2 float64) bool {
  253. return math.Dim(f1, f2) < MIN
  254. }
  255. func Precision(f float64, prec int, round bool) float64 {
  256. pow10_n := math.Pow10(prec)
  257. if round {
  258. return math.Trunc((f+0.5/pow10_n)*pow10_n) / pow10_n
  259. }
  260. return math.Trunc((f)*pow10_n) / pow10_n
  261. }