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

169 lines
6.2 KiB
Python

from sqlalchemy.orm import noload
from sqlalchemy.sql._typing import ColumnExpressionArgument
from typing import List, Dict, Any, Optional, Set
from iti.applications.extensions import db
from flask import current_app
from iti.applications.models import (
IotAlertLog,
IotAlertRule,
IotAlertPush,
IotNode,
IotEndpoint,
)
from sqlalchemy import select, distinct, desc
from simpleeval import simple_eval
def delete_node_alert_rule(node: IotNode):
"""
删除节点告警规则
Args:
node: 节点对象
"""
db.session.query(IotAlertRule).filter_by(node_id=node.id).delete()
db.session.commit()
def add_endpoint_alert_log(endpoint_id: int):
"""
添加采集端告警日志
Args:
endpoint_id: 采集端ID
"""
endpoint = db.session.scalar(select(IotEndpoint).filter_by(id=endpoint_id))
if not endpoint:
return "采集端不存在"
alert_tag = f"ep{endpoint.id}_nd0"
alert_log = db.session.scalar(select(IotAlertLog).filter_by(alert_tag=alert_tag))
if alert_log:
alert_log.trigger_count += 1
if alert_log.trigger_count >= 3:
alert_log.status = 1
push_alert(alert_log)
else:
dict_data = dict(
alert_tag=alert_tag,
alert_target_name=f"{endpoint.endpoint_name}({endpoint.endpoint_number})",
alert_level=1,
status=0,
trigger_count=1,
alert_content=f"采集端 {endpoint.endpoint_name}({endpoint.endpoint_number}) 网络不可达",
)
alert_log = IotAlertLog(**dict_data)
db.session.add(alert_log)
db.session.commit()
return ""
def add_node_alert_log(node_id: int, alert_value: str):
"""
添加节点值异常告警日志
Args:
node_id: 节点ID
alert_value: 告警值
"""
node = db.session.scalar(select(IotNode).filter_by(id=node_id))
if not node:
return "节点不存在"
alert_rule_list = db.session.scalars(select(IotAlertRule).filter_by(node_id=node_id).order_by(desc(IotAlertRule.alert_level))).all()
if len(alert_rule_list) > 0:
for alert_rule in alert_rule_list:
if alert_rule.status == 1:
# 根据data_type转换alert_value
value_data = get_value(node.data_type, alert_value)
# 检查告警值是否满足触发规则
if is_alert_trigger(alert_rule.alert_rule, value_data):
alert_tag = f"ep{node.endpoint_id}_nd{node_id}"
alert_log = db.session.scalar(select(IotAlertLog).filter_by(alert_tag=alert_tag))
if alert_log:
alert_log.trigger_count += 1
alert_log.alert_content = complete_alert_text(node, alert_rule, alert_value)
if alert_log.trigger_count >= alert_rule.trigger_count:
alert_log.status = 1
push_alert(alert_log)
else:
dict_data = dict(
alert_tag=alert_tag,
alert_target_name=f"采集节点({node.node_number})",
alert_level=alert_rule.alert_level,
status=0,
trigger_count=1,
alert_content=complete_alert_text(node, alert_rule, alert_value),
)
alert_log = IotAlertLog(**dict_data)
db.session.add(alert_log)
db.session.commit()
break
return ""
def get_value(data_type: str, alert_value: str):
"""
获取告警值
Args:
data_type: 数据类型
alert_value: 告警值
"""
if data_type == "int":
return int(alert_value)
elif data_type == "float":
return float(alert_value)
elif data_type == "double":
return float(alert_value)
else:
return alert_value
def is_alert_trigger(alert_rule_text: str, value_data: Any):
"""
检查告警值是否满足触发规则
Args:
alert_rule_text: 告警规则文本
value_data: 告警值
"""
# 解析告警规则文本,判断是否满足触发规则
try:
result = simple_eval_expression(alert_rule_text, {'x': value_data})
return result
except (NameError, SyntaxError, TypeError) as e:
current_app.logger.error(f"评估表达式 {alert_rule_text} 时出错: {e}")
return False
def push_alert(alert_log: IotAlertLog):
"""
推送告警消息
Args:
alert_log: 告警日志
"""
level = f"{alert_log.alert_level}"
alert_push_list = db.session.scalars(select(IotAlertPush).filter(IotAlertPush.alert_level.contains(level)).filter_by(status=1)).all()
if alert_push_list:
for alert_push in alert_push_list:
# TODO 调用地址发送告警消息
current_app.logger.info(f"推送告警消息到 {alert_push.push_url},内容:{alert_log.alert_content}")
def complete_alert_text(node: IotNode, alert_rule: IotAlertRule, alert_value: str):
"""
完善告警内容文本
Args:
node: 节点
alert_rule: 告警规则
alert_value: 告警值
"""
alert_text_temp = alert_rule.alert_text or ""
alert_text_temp = alert_text_temp.replace("{_workshopName_}", node.workshop.workshop_name+"("+node.workshop.workshop_number+")")
alert_text_temp = alert_text_temp.replace("{_deviceName_}", node.device.device_name+"("+node.device.device_number+")")
alert_text_temp = alert_text_temp.replace("{_endpointName_}", node.endpoint.endpoint_name+"("+node.endpoint.endpoint_number+")")
alert_text_temp = alert_text_temp.replace("{_mark_}", node.mark)
alert_text_temp = alert_text_temp.replace("{_nodeNumber_}", node.node_number)
alert_text_temp = alert_text_temp.replace("{_alertValue_}", alert_value)
return alert_text_temp
def simple_eval_expression(expr, variables):
"""
简单地评估包含变量的字符串表达式
:param expr: 字符串表达式,如 "x > 5 and y < 10"
:param variables: 变量字典,如 {'x': 7, 'y': 3}
:return: 表达式结果
"""
return simple_eval(expr, names=variables)