et-go 20240919重建
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
5.5 KiB

1 month ago
package et_analyze
import (
"encoding/json"
"fmt"
"gitea.anxinyun.cn/container/common_models"
"gitea.anxinyun.cn/container/common_utils"
"gitea.anxinyun.cn/container/common_utils/configLoad"
"gitea.anxinyun.cn/container/common_utils/kafkaHelper"
"log"
"strconv"
)
type AggThresholdHandler struct {
alarmTopic string
configHelper *common_utils.ConfigHelper
kafkaAsyncProducer *kafkaHelper.KafkaAsyncProducer
}
func NewAggThresholdHandler() *AggThresholdHandler {
configYaml := configLoad.LoadConfig()
redisAdd := configYaml.GetString("redis.address")
kafkaBrokers := configYaml.GetStringSlice("kafka.brokers")
alarmTopic := configYaml.GetString("kafka.topics.alarm_anxinyun")
return &AggThresholdHandler{
alarmTopic: alarmTopic,
configHelper: common_utils.NewConfigHelper(redisAdd),
kafkaAsyncProducer: kafkaHelper.NewKafkaAsyncProducer(kafkaBrokers),
}
}
// ProcessData 进行 aggData 阈值分析,向 kafka 发送 alarm 消息
func (t *AggThresholdHandler) ProcessData(aggData *common_models.AggData) {
if aggData == nil || aggData.Changed == nil || len(aggData.Changed) == 0 {
log.Printf("aggData 非法数据:[%v]\n", aggData)
return
}
alarmMsg := t.judgeThreshold(aggData)
if alarmMsg != nil {
stationInfo, _ := t.configHelper.GetStationInfo(aggData.SensorId)
alarmMsg.Sponsor = common_models.Alarm_Sponsor_Recv
jsonData, err := json.Marshal(alarmMsg)
if err != nil {
fmt.Printf("测点[%d-%s][kafka-topic:%s][%v]Error marshalling JSON:%s \n", stationInfo.Id, stationInfo.Name, t.alarmTopic, alarmMsg, err)
}
// 发布 kafka 消息
fmt.Printf("测点[%d-%s][kafka-topic:%s]%s\n", stationInfo.Id, stationInfo.Name, t.alarmTopic, jsonData)
t.kafkaAsyncProducer.Publish(t.alarmTopic, jsonData)
}
}
func (t *AggThresholdHandler) judgeThreshold(aggData *common_models.AggData) *common_models.AlarmMsg {
var aggTypeMap = map[int]string{
2001: "日聚集",
2002: "周聚集",
2003: "月聚集",
2004: "年聚集",
2005: "时聚集",
2006: "10分钟聚集",
}
aggTypeStr := aggTypeMap[aggData.AggTypeId]
if aggTypeStr == "" {
aggTypeStr = "其他聚集"
}
// 检查测点是否有聚集阈值配置信息
aggThreshold, err := t.configHelper.GetAggThreshold(aggData.StructId, aggData.FactorId)
if err != nil || aggThreshold.Items == nil || len(aggThreshold.Items) == 0 {
log.Printf("未配置aggThreshold,无须进行阈值判断:[structId:%d factorId:%d] \n", aggData.StructId, aggData.FactorId)
return nil
}
factor, err := t.configHelper.GetFactorInfo(aggData.FactorId)
if err != nil || factor.Items == nil || len(factor.Items) == 0 {
log.Printf("获取factor异常:[structId:%d factorId:%d]Error: %v \n", aggData.StructId, aggData.FactorId, err)
return nil
}
var ls []common_models.ThresholdAlarmDetail
// Changed map[string]float64 // 变化量
for fieldName, val := range aggData.Changed {
protoItem := factor.GetProtoItem(fieldName)
if protoItem == nil {
log.Printf("测点[%d-%d-%d][%s]aggData[%v],但是Redis中没有对应的[protoItem:%s]。\n",
aggData.StructId, aggData.FactorId, aggData.SensorId, aggData.Date, aggData, fieldName)
} else {
protoItemThs := aggThreshold.GetThresholdsByItem(aggThreshold.Items, protoItem.Id)
overThresholdItem := aggThreshold.FindThresholdInRange(protoItemThs, val)
if overThresholdItem != nil {
// content 格式如:应变的10分钟聚集变化率:-0.60με,超1级阈值[-1~-0.5]
content := fmt.Sprintf("%s的%s变化率:%.2f%s,超%d级阈值[%s]", protoItem.Name, aggTypeStr, val, protoItem.UnitName, overThresholdItem.Level, overThresholdItem.RangeText())
ls = append(ls, common_models.ThresholdAlarmDetail{Level: overThresholdItem.Level, Content: content})
}
//log.Printf("[aggThreshold judgeThreshold] 测点[sensorId: %d] fieldName: %s, ChangedVal (float64): %f\n", aggData.SensorId, fieldName, val)
}
}
alarmMsg := t.getAndCacheAlarmMsg(aggData, ls)
return alarmMsg
}
func (t *AggThresholdHandler) getAndCacheAlarmMsg(aggData *common_models.AggData, ls []common_models.ThresholdAlarmDetail) *common_models.AlarmMsg {
stationInfo, err := t.configHelper.GetStationInfo(aggData.SensorId)
if err != nil {
log.Printf("[%v]测点不存在\n", aggData)
return nil
}
if ls != nil && len(ls) > 0 {
// 超阈值告警
alarm := common_models.NewOverChangingRateThreshold(findMinLevel(ls), stringifyThresholds(ls))
if alarm == nil {
//log.Printf("[%v] over-agg-threshold:[Level:%d] content:[%s]ERROR: 未找到对应告警等级的告警码。", aggData, alarm.Level, alarm.Content)
1 month ago
return nil
}
log.Printf("[%v] over-agg-threshold:[Level:%d] content:[%s]", aggData, alarm.Level, alarm.Content)
// 将测点信息 -> 告警信息
alarmMsg := alarm.ToAlarmMsg(stationInfo, aggData.Date)
// Redis 中添加告警记录, key = alarm:2:100
redisKey := common_models.AlarmRedisKey(common_models.ALARM_SOURCE_STATION, strconv.Itoa(aggData.SensorId))
t.configHelper.SAddAlarm(redisKey, alarm.AlarmType)
return &alarmMsg
} else {
// Redis 中删除告警记录,key = alarm:2:100
redisKey := common_models.AlarmRedisKey(common_models.ALARM_SOURCE_STATION, strconv.Itoa(aggData.SensorId))
affects := t.configHelper.SRemAlarm(redisKey, common_models.Alarm_Type_Over_ChangeRate_Threshold)
// 如果Redis中存在告警,则要发送恢复告警
if affects > 0 {
alarm := common_models.NewRecoverChangingRateThreshold()
alarmMsg := alarm.ToAlarmMsg(stationInfo, aggData.Date)
return &alarmMsg
}
}
return nil
}