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)