from __future__ import annotations import logging import xml.etree.ElementTree as ET from typing import Dict, Callable, Any, Optional import os from enum import Enum, auto from equipment_calculation.expressioncalculator import ExpressionCalculator import copy # 配置logging logging.basicConfig( level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[logging.FileHandler("bcl_calculator.log"), logging.StreamHandler()], ) class BCLVariantType(Enum): TEXT = "文本" FLOAT = "浮点数" INTEGER = "整形" BOOLEAN = "布尔类型" DATASOURCE = "数据源" EXPRESSION = "表达式" # NULL = "NULL" class BCLOperatorType(Enum): """ BCL运算符类型枚举 """ ADD = "+" # 加 SUBTRACT = "-" # 减 MULTIPLY = "*" # 乘 DIVIDE = "/" # 除 AND = "&&" # 与 OR = "||" # 或 NOT = "!" # 非 EQUAL = "==" # 等于 NOT_EQUAL = "!=" # 不等于 GREATER = ">" GREATER_EQUAL = ">=" LESS = "<" LESS_EQUAL = "<=" UNKNOWN = "unknown" # 未知运算符 def str_to_operator(op_str: str) -> BCLOperatorType: """ 将字符串转换为BCLOperatorType枚举 :param op_str: 运算符字符串 :return: 对应的BCLOperatorType枚举值 """ op_map = { ">": BCLOperatorType.GREATER, ">=": BCLOperatorType.GREATER_EQUAL, "<": BCLOperatorType.LESS, "<=": BCLOperatorType.LESS_EQUAL, "+": BCLOperatorType.ADD, "-": BCLOperatorType.SUBTRACT, "*": BCLOperatorType.MULTIPLY, "/": BCLOperatorType.DIVIDE, "&&": BCLOperatorType.AND, "||": BCLOperatorType.OR, "!": BCLOperatorType.NOT, "==": BCLOperatorType.EQUAL, "!=": BCLOperatorType.NOT_EQUAL, "加": BCLOperatorType.ADD, "减": BCLOperatorType.SUBTRACT, "乘": BCLOperatorType.MULTIPLY, "除": BCLOperatorType.DIVIDE, "与": BCLOperatorType.AND, "或": BCLOperatorType.OR, "非": BCLOperatorType.NOT, "等于": BCLOperatorType.EQUAL, "不等于": BCLOperatorType.NOT_EQUAL, } if op_str is None: return BCLOperatorType.UNKNOWN return op_map.get(op_str.strip(), BCLOperatorType.UNKNOWN) class BCLVariant: def _setobject(self, var_type: BCLVariantType, value): self.type = var_type self.value = value def __init__(self, value: Any): """ 根据value的类型自动确定变量类型 :param value: 变量值,可以是str, ET.Element, float, int, bool或list类型 """ if isinstance(value, BCLVariant): return self._setobject(value.type, value.value) if isinstance(value, str): return self._setobject(BCLVariantType.TEXT, value) elif isinstance(value, ET.Element): return self._setobject(BCLVariantType.EXPRESSION, value) elif isinstance(value, list): return self._setobject(BCLVariantType.DATASOURCE, value) elif isinstance(value, bool): return self._setobject(BCLVariantType.BOOLEAN, value) elif isinstance(value, float): return self._setobject(BCLVariantType.FLOAT, value) elif isinstance(value, int): return self._setobject(BCLVariantType.INTEGER, value) elif value == None: # return self._setobject(BCLVariantType.NULL, "") return self._setobject(BCLVariantType.TEXT, "") else: raise ValueError(f"不支持的类型: {type(value)}") def get_value(self) -> Any: """ 获取变量值 :return: 变量值,转换为浮点数 """ try: if self.type == BCLVariantType.TEXT: return self.value elif self.type == BCLVariantType.DATASOURCE: return self.value.get_value() elif self.type == BCLVariantType.FLOAT: return float(self.value) elif self.type == BCLVariantType.INTEGER: return int(self.value) elif self.type == BCLVariantType.EXPRESSION: return self.value elif self.type == BCLVariantType.BOOLEAN: return bool(self.value) else: raise ValueError(f"未知的变量类型: {self.type}") except (ValueError, TypeError) as e: raise ValueError(f"获取变量的值发生错误: {str(e)}\n") def __str__(self) -> str: """ 返回变量的字符串表示 :return: 变量的字符串表示 """ return f"Variant({self.type}, {self.value})" # BCL上下文类(用于供计算过程中从该类中获取变量值) class BCLContext: def __init__(self, valueDict: Dict[str, Any] = None, **kwargs): self.variables = {} # 变量字典,键为变量名,值为BCLVariant对象 self.params = {} # 参数字典,键为字符串,单独存储参数 if valueDict: for key, value in valueDict.items(): if isinstance(value, BCLVariant): self.variables[key] = value else: self.variables[key] = BCLVariant(value.__dict__) def _get_variable_value(self, var_name: str) -> BCLVariant: """ 获取变量值 :param var_name: 变量名,格式为'对象.属性' :return: 变量值,如果变量不存在则返回None,请勿抛出异常 """ return self.variables.get(var_name) def get_variable_value(self, var_name: str) -> BCLVariant: """ 获取变量值 :param var_name: 变量名,格式为'对象.属性' :return: 变量值,如果变量不存在则返回None,请勿抛出异常 """ var = self._get_variable_value(var_name) if var: return var else: return None def set_param(self, key: str, value): """ 设置参数到参数字典 :param key: 参数键,必须为字符串 :param value: 参数值 """ if not isinstance(key, str): raise TypeError("参数键必须是字符串类型") self.params[key] = value def get_param(self, key: str, default=None): """ 从参数字典获取参数 :param key: 参数键,必须为字符串 :param default: 默认值,如果参数不存在则返回 :return: 参数值或默认值 """ if not isinstance(key, str): raise TypeError("参数键必须是字符串类型") return self.params.get(key, default) # 链式BCL上下文类,用于将两个上下文对象穿成一个链表,从而实现依次从两个上下文中获取变量值(该类是抽象类,不支持直接使用) class BCLPrevContext(BCLContext): def __init__(self, prevContext: BCLContext = None): super().__init__() self._prevContext = prevContext def _get_variable_value(self, var_name: str) -> BCLVariant: """ 获取变量值,支持点号分隔的变量名查询 :param var_name: 变量名,可以是简单变量名或'对象.属性'格式 :return: 找到的变量或None """ result = super()._get_variable_value(var_name) if result == None and self._prevContext: # 如果当前对象没有找到变量,尝试在前一个上下文中查找 try: result = self._prevContext._get_variable_value(var_name) except ValueError: pass return result def set_param(self, key: str, value): """ 设置参数到参数字典 :param key: 参数键,必须为字符串 :param value: 参数值 """ if not isinstance(key, str): raise TypeError("参数键必须是字符串类型") if self._prevContext: self._prevContext.set_param(key, value) else: super().set_param(key, value) def get_param(self, key: str, default=None): """ 从参数字典获取参数 :param key: 参数键,必须为字符串 :param default: 默认值,如果参数不存在则返回 :return: 参数值或默认值 """ if not isinstance(key, str): raise TypeError("参数键必须是字符串类型") result = super().get_param(key, default) if result == None and self._prevContext: result = self._prevContext.get_param(key, default) return result # 前缀BCL上下文类,用于将一个上下文对象与一个前缀绑定,从而实现从该上下文中获取变量值 class BCLPrefixContext(BCLContext): def __init__(self, prefix: str, childContext: BCLContext): self._childContext = childContext self._prefix = prefix def __init__(self, prefix: str, valueDict: Dict[str, Any] = None): self._childContext = BCLContext(valueDict) self._prefix = prefix def _get_variable_value(self, var_name: str) -> BCLVariant: """ 获取变量值,支持点号分隔的变量名查询 :param var_name: 变量名,可以是简单变量名或'对象.属性'格式 :return: 找到的变量或None """ result = None # 检查是否是点号分隔的两段式变量名 if "." in var_name: parts = var_name.split(".", maxsplit=1) if len(parts) >= 2 and parts[0] == self._prefix: # 如果是当前数据对象的属性,则查询对应的变量 try: result = self._childContext._get_variable_value(parts[1]) except ValueError: pass return result # 前缀链式BCL上下文类,用于将一个上下文对象与一个前缀绑定,从而实现从该上下文中获取变量值 class BCLPrefixPrevContext(BCLPrefixContext, BCLPrevContext): def __init__(self, prefix: str, childContext: BCLContext, prevContext: BCLContext = None): BCLPrefixContext.__init__(self, prefix, childContext) BCLPrevContext.__init__(self, prevContext) def __init__(self, prefix: str, valueDict: Dict[str, Any], prevContext: BCLContext = None): BCLPrefixContext.__init__(self, prefix, valueDict) BCLPrevContext.__init__(self, prevContext) def _get_variable_value(self, var_name: str) -> BCLVariant: """ 获取变量值,支持点号分隔的变量名查询 :param var_name: 变量名,可以是简单变量名或'对象.属性'格式 :return: 找到的变量或None """ result = BCLPrefixContext._get_variable_value(self, var_name) if result == None: # 如果当前对象没有找到变量,尝试在前一个上下文中查找 try: result = BCLPrevContext._get_variable_value(self, var_name) except ValueError: pass return result class BCLDataSourceItem: def __init__(self, valueDict: Any, parent: BCLDataSourceItem = None, data_name: str = ""): self.variables = {} # 变量字典,键为变量名,值为BCLVariant对象 self._parent = parent self._childs = [] self._dataname: str = data_name # 处理值字典 if valueDict: items = valueDict if isinstance(valueDict, Dict) else valueDict.__dict__ # 将所有属性添加到变量字典中 for key, value in items.items(): if isinstance(value, BCLVariant): self.variables[key] = value else: self.variables[key] = BCLVariant(value) # 检查是否有子节点列表(children 或其他名称) if "children" in items: child_nodes = items["children"] if isinstance(child_nodes, list): # 为每个子节点创建 BCLDataSourceItem 对象 self._childs = [BCLDataSourceItem(child, self) for child in child_nodes] def get_dataname(self) -> str: return self._dataname def get_value(self, var_name: str) -> BCLVariant: return self.variables.get(var_name) def get_parent(self): if self._parent: return self._parent else: return None # return self._parent def get_items(self) -> list: return self._childs def set_parent(self, parent: BCLDataSourceItem): self._parent = parent def set_childs(self, childs: list): self._childs = childs class BCLDataSourceContext(BCLPrevContext): def __init__(self, data_source: list[BCLDataSourceItem], prevContext: BCLContext = None): super().__init__(prevContext) self._data_source = data_source def _get_variable_value(self, var_name: str) -> BCLVariant: result = None if var_name == "source": return BCLVariant(self._data_source) else: return super()._get_variable_value(var_name) class BCLCalcItemContext(BCLPrevContext): def __init__(self, data_name: str, data_source: list[BCLDataSourceItem], prevContext: BCLContext = None): super().__init__(prevContext) self._index = 0 self._data_name = data_name self._data_source = data_source def _get_variable_value(self, var_name: str) -> BCLVariant: """ 获取变量值,支持点号分隔的变量名查询 :param var_name: 变量名,可以是简单变量名或'对象.属性'格式 :return: 找到的变量或None """ result = None # 检查是否是点号分隔的两段式变量名 if "." in var_name: parts = var_name.split(".", maxsplit=1) prefix_name = parts[0] # 如果是当前数据对象的属性,则查询对应的变量 try: if prefix_name == "parent": value_name = parts[1] child_pairt = parts[1] item = self._data_source[self._index - 1].get_parent() # 处理可能的多级属性访问 while "." in child_pairt and item: child_parts = child_pairt.split(".", maxsplit=1) if child_parts[0] == "parent": item = item.get_parent() value_name = child_parts[1] child_pairt = child_parts[1] else: break if item: result = BCLVariant(item.get_value(value_name)) else: result = BCLVariant("") elif prefix_name == self._data_name or prefix_name == "curnode": result = BCLVariant(self._data_source[self._index - 1].get_value(parts[1])) elif ( self._data_source[self._index - 1].get_parent() and prefix_name == self._data_source[self._index - 1].get_parent().get_dataname() ): result = BCLVariant(self._data_source[self._index - 1].get_parent().get_value(parts[1])) except ValueError: pass if result == None: try: if var_name == "curnode": result = BCLVariant(self._data_source[self._index - 1]) elif var_name == "parent": result = BCLVariant(self._data_source[self._index - 1].get_parent()) elif var_name == "items": result = BCLVariant(self._data_source[self._index - 1].get_items()) elif ( self._data_source[self._index - 1].get_parent() and var_name == self._data_source[self._index - 1].get_parent().get_dataname() ): result = BCLVariant(self._data_source[self._index - 1].get_parent()) else: result = super()._get_variable_value(var_name) except ValueError: pass return result def __iter__(self): return self def __next__(self): if self._index >= len(self._data_source): raise StopIteration value = self._data_source[self._index] self._index += 1 return value # 定义test函数 def test_func(funcname: str, claculate, context: BCLContext, *args): logging.debug(f"call function : {funcname}") return 0 def round_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 2: raise ValueError(f"{funcname}函数需要2个参数,但提供了{len(args)}个参数") # 检查第一个参数类型(浮点数或表达式) if not isinstance(args[0], BCLVariant) or args[0].type not in (BCLVariantType.FLOAT, BCLVariantType.EXPRESSION): raise ValueError(f"{funcname}函数的第一个参数必须是浮点数或表达式类型") # 检查第二个参数类型(整数或表达式) if not isinstance(args[1], BCLVariant) or args[1].type not in (BCLVariantType.INTEGER, BCLVariantType.EXPRESSION): raise ValueError(f"{funcname}函数的第二个参数必须是整数或表达式类型") # 获取参数值 value = args[0].value decimal_places = args[1].value # print(f"call function : {funcname}\n") logging.debug(f"四舍五入值: {value}, 小数位数: {decimal_places}") # 执行四舍五入计算 return BCLVariant(round(float(value), int(decimal_places))) def calc_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 1: raise ValueError(f"{funcname}函数需要1个参数,但提供了{len(args)}个参数") # 检查参数类型(表达式) if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.EXPRESSION: raise ValueError(f"{funcname}函数的参数必须是表达式类型") # 获取参数值 expression = args[0].value # print(f"call function : {funcname}\n") logging.debug(f"计算表达式: {expression}") # 执行表达式计算并返回结果 return BCLVariant(expression) def sort_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 4: raise ValueError(f"{funcname}函数需要4个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.DATASOURCE: raise ValueError(f"{funcname}函数的第一个参数必须是数据源类型") if not isinstance(args[1], BCLVariant) or args[1].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第二个参数必须是文本类型") if not isinstance(args[2], BCLVariant) or args[2].type != BCLVariantType.EXPRESSION: raise ValueError(f"{funcname}函数的第三个参数必须是表达式类型") if not isinstance(args[3], BCLVariant) or args[3].type != BCLVariantType.STRING: raise ValueError(f"{funcname}函数的第四个参数必须是字符串类型") # 获取参数值 data_source = args[0].value text_param = args[1].value expression = args[2].value string_param = args[3].value logging.debug(f"call function : {funcname}\n") logging.debug(f"数据源: {data_source}") logging.debug(f"文本参数: {text_param}") logging.debug(f"表达式: {expression}") logging.debug(f"字符串参数: {string_param}") temp_calculator = copy.copy(claculate) # 计算排序键并存储原始索引 sorted_items = [] temp_context = BCLCalcItemContext(text_param, data_source, context) for index, item in enumerate(temp_context): try: # 计算排序表达式的值 result = temp_calculator.evaluate_node(expression, temp_context) if not isinstance(result, BCLVariant): raise TypeError("排序表达式计算结果必须是BCLVariant类型") if result.type not in (BCLVariantType.INTEGER, BCLVariantType.FLOAT): raise TypeError(f"排序表达式计算结果类型必须是数字,实际是{result.type}") sorted_items.append((float(result.value if result.value else "0"), index, item)) except Exception as e: logging.error(f"计算排序键时出错: {e}", exc_info=True) # 出错项放在最后 sorted_items.append((float("inf"), index, item)) # 根据排序键和方向排序 reverse_order = string_param.lower() == "desc" sorted_items.sort(key=lambda x: (x[0], x[1]), reverse=reverse_order) # 提取排序后的数据源 sorted_data = [item for (key, index, item) in sorted_items] # 返回排序后的结果 return BCLVariant(sorted_data) def isempty_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 1: raise ValueError(f"{funcname}函数需要1个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.DATASOURCE: raise ValueError(f"{funcname}函数的第一个参数必须是数据源类型") # 获取参数值 data_source = args[0].value logging.debug(f"call function : {funcname}\n") logging.debug(f"数据源: {data_source}\n") # 检查数据源是否为空 is_empty = len(data_source) == 0 # 返回检查结果 return BCLVariant(is_empty) def setparam_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 2: raise ValueError(f"{funcname}函数需要2个参数,但提供了{len(args)}个参数") # 检查第一个参数类型 (文本或表达式) if not isinstance(args[0], BCLVariant) or args[0].type not in (BCLVariantType.TEXT, BCLVariantType.EXPRESSION): raise ValueError(f"{funcname}函数的第一个参数必须是文本或表达式类型") # 检查第二个参数类型 (BCLVariant) if not isinstance(args[1], BCLVariant): raise ValueError(f"{funcname}函数的第二个参数必须是BCLVariant类型") # 获取参数值 param_name_var = args[0] param_value = args[1] if param_name_var.type == BCLVariantType.EXPRESSION: # 计算参数名 (如果是表达式则求值) calculator = BCLCalculator() param_name_result = calculator._evaluate_node(param_name_var.value, context) if param_name_result.type != BCLVariantType.TEXT: raise TypeError(f"{funcname}函数的第一个参数表达式必须返回文本类型") param_name = param_name_result.value else: param_name = param_name_var.value if param_value.type == BCLVariantType.EXPRESSION: # 计算参数名 (如果是表达式则求值) calculator = BCLCalculator() param_value_result = calculator._evaluate_node(param_value.value, context) if param_value_result.type != BCLVariantType.TEXT: raise TypeError(f"{funcname}函数的第二个参数表达式必须返回文本类型") param_value = param_value_result.value # 设置参数到上下文 context.set_param(param_name, param_value) # 返回设置的参数值 return param_value def getparam_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 1: raise ValueError(f"{funcname}函数需要1个参数,但提供了{len(args)}个参数") # 检查参数类型 (文本或表达式) if not isinstance(args[0], BCLVariant) or args[0].type not in (BCLVariantType.TEXT, BCLVariantType.EXPRESSION): raise ValueError(f"{funcname}函数的参数必须是文本或表达式类型") # 获取参数值 param_name_var = args[0] # 计算参数名 (如果是表达式则求值) calculator = BCLCalculator() if param_name_var.type == BCLVariantType.EXPRESSION: param_name_result = calculator._evaluate_node(param_name_var.value, context) if param_name_result.type != BCLVariantType.TEXT: raise TypeError(f"{funcname}函数的参数表达式必须返回文本类型") param_name = param_name_result.value else: param_name = param_name_var.value # 从上下文获取参数 param_value = context.get_param(param_name) # 返回参数值,如果不存在则返回NULL类型 if param_value is None: return BCLVariant(None, BCLVariantType.NULL) return param_value def groupby_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 4: raise ValueError(f"{funcname}函数需要4个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.DATASOURCE: raise ValueError(f"{funcname}函数的第一个参数必须是数据源类型") if not isinstance(args[1], BCLVariant) or args[1].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第二个参数必须是文本类型") if not isinstance(args[2], BCLVariant) or args[2].type not in (BCLVariantType.TEXT, BCLVariantType.EXPRESSION): raise ValueError(f"{funcname}函数的第三个参数必须是文本或表达式类型") if not isinstance(args[3], BCLVariant) or args[3].type != BCLVariantType.EXPRESSION: raise ValueError(f"{funcname}函数的第四个参数必须是表达式类型") # 获取参数值 data_source = args[0].value text_param = args[1].value group_param = args[2].value calc_expression = args[3].value logging.debug(f"call function : {funcname}\n") logging.debug(f"分组参数: {group_param}") logging.debug(f"计算表达式: {calc_expression}") temp_calculator = copy.copy(claculate) groups = {} # 遍历数据源进行分组 temp_context = BCLCalcItemContext(text_param, data_source, context) for index, item in enumerate(temp_context): try: # 根据第三个参数类型获取分组键 if args[2].type == BCLVariantType.TEXT: # 文本类型直接作为键 group_key = group_param else: # 表达式类型计算结果作为键 result = temp_calculator.evaluate_node(group_param, temp_context) if not isinstance(result, BCLVariant): raise TypeError("分组表达式计算结果必须是BCLVariant类型") group_key = str(result.value) # 添加到对应分组 if group_key not in groups: groups[group_key] = [] groups[group_key].append(item) except Exception as e: logging.error(f"分组处理出错: {e}", exc_info=True) # 错误项放入'error'分组 if "error" not in groups: groups["error"] = [] groups["error"].append(item) # 对每个分组执行第四个表达式计算 group_results = {} group_context = BCLCalcItemContext(text_param, groups.items(), context) for index, (key, items) in enumerate(group_context): try: # 计算表达式结果 result = temp_calculator.evaluate_node(calc_expression, group_context) group_results[key] = {"items": items, "result": result.value if isinstance(result, BCLVariant) else None} except Exception as e: logging.error(f"分组计算出错: {e}", exc_info=True) group_results[key] = {"items": items, "result": None} # 返回分组计算结果 return BCLVariant(group_results) def strfind_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 2: raise ValueError(f"{funcname}函数需要2个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第一个参数必须是文本类型") if not isinstance(args[1], BCLVariant) or args[1].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第二个参数必须是文本类型") # 获取参数值 text1 = args[0].value text2 = args[1].value # print(f"call function : {funcname}\n") logging.debug(f"文本1: {text1}") logging.debug(f"文本2: {text2}") # 查找子字符串位置 position = text1.find(text2) # 返回查找结果 return BCLVariant(position) def in_func(funcname: str, claculate, context: BCLContext, *args): # 参数数量校验 if len(args) != 2: raise ValueError(f"函数 {funcname} 需要传入2个参数,实际传入了{len(args)}个参数") # 参数类型校验 param1 = args[0] param2 = args[1] # 验证参数1:文本或表达式类型 if not (isinstance(param1, BCLVariant) and param1.type in (BCLVariantType.TEXT, BCLVariantType.EXPRESSION)): raise TypeError(f"函数 {funcname} 的第1个参数必须是文本或表达式类型,实际类型为{type(param1).__name__}") # 验证参数2:文本或表达式类型 if not (isinstance(param2, BCLVariant) and param2.type in (BCLVariantType.TEXT, BCLVariantType.EXPRESSION)): raise TypeError(f"函数 {funcname} 的第2个参数必须是文本或表达式类型,实际类型为{type(param2).__name__}") # 调试信息 logging.debug(f"调用 {funcname} 函数,参数1: {param1}, 参数2: {param2}") # 创建计算器实例 temp_calculator = copy.copy(claculate) try: # 计算两个表达式的值 value1 = ( temp_calculator.calculate_expression(param1.value) if param1.type == BCLVariantType.EXPRESSION else param1.value ) value2 = ( temp_calculator.calculate_expression(param2.value) if param2.type == BCLVariantType.EXPRESSION else param2.value ) # 判断value1是否在value2中 result = value1 in value2 # 返回布尔类型的BCLVariant对象 return BCLVariant(result) except Exception as e: logging.error(f"{funcname} 函数执行错误: {str(e)}", exc_info=True) raise def iif_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 3: raise ValueError(f"{funcname}函数需要3个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type not in (BCLVariantType.BOOLEAN, BCLVariantType.EXPRESSION): raise ValueError(f"{funcname}函数的第一个参数必须是布尔或表达式类型") if not isinstance(args[1], BCLVariant) or args[1].type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.EXPRESSION, ): raise ValueError(f"{funcname}函数的第二个参数必须是表达式类型") if not isinstance(args[2], BCLVariant) or args[2].type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.EXPRESSION, ): raise ValueError(f"{funcname}函数的第三个参数必须是表达式类型") # 获取参数值 condition_param = args[0] true_expr = args[1] false_expr = args[2] # print(f"call function : {funcname}\n") temp_calculator = copy.copy(claculate) try: # 计算条件结果 if condition_param.type == BCLVariantType.EXPRESSION: condition_result = temp_calculator.evaluate_node(condition_param.value, context) if condition_result.type != BCLVariantType.BOOLEAN: raise TypeError("条件表达式必须返回布尔值") condition_value = condition_result.value else: condition_value = condition_param.value # 根据条件计算结果 if condition_value: if true_expr.type == BCLVariantType.EXPRESSION: result = temp_calculator.evaluate_node(true_expr.value, context) else: result = true_expr else: if false_expr.type == BCLVariantType.EXPRESSION: result = temp_calculator.evaluate_node(false_expr.value, context) else: result = false_expr return result except Exception as e: print(f"iif函数计算出错: {e}") raise e def for_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 4: raise ValueError(f"{funcname}函数需要4个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.DATASOURCE: raise ValueError(f"{funcname}函数的第一个参数必须是数据源类型") if not isinstance(args[1], BCLVariant) or args[1].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第二个参数必须是文本类型") if not isinstance(args[2], BCLVariant) or args[2].type != BCLVariantType.EXPRESSION: raise ValueError(f"{funcname}函数的第三个参数必须是表达式类型") if not isinstance(args[3], BCLVariant) or args[3].type != BCLVariantType.EXPRESSION: raise ValueError(f"{funcname}函数的第四个参数必须是表达式类型") # 获取参数值 data_source = args[0].value loop_var_name = args[1].value expr1 = args[2].value expr2 = args[3].value logging.debug(f"call function : {funcname}\n") logging.debug(f"数据源: {data_source}\n") logging.debug(f"循环变量名: {loop_var_name}") logging.debug(f"表达式1: {expr1}") logging.debug(f"表达式2: {expr2}") temp_calculator = copy.copy(claculate) result = None temp_context = BCLCalcItemContext(loop_var_name, data_source, context) for index, item in enumerate(temp_context): try: # 执行第一个表达式 result1 = temp_calculator.evaluate_node(expr1, temp_context) except Exception as e: logging.error(f"循环处理出错: {e}", exc_info=True) continue if args[3].type == BCLVariantType.EXPRESSION: result = claculate.evaluate_node(expr2, context) # 返回循环结果 return BCLVariant(result) def group_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 3: raise ValueError(f"{funcname}函数需要3个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.DATASOURCE: raise ValueError(f"{funcname}函数的第一个参数必须是数据源类型") if not isinstance(args[1], BCLVariant) or args[1].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第二个参数必须是文本类型") if not isinstance(args[2], BCLVariant) or args[2].type not in (BCLVariantType.TEXT, BCLVariantType.EXPRESSION): raise ValueError(f"{funcname}函数的第三个参数必须是文本或表达式类型") # 获取参数值 data_source = args[0].value text_param = args[1].value third_param = args[2].value logging.debug(f"call function : {funcname}\n") logging.debug(f"数据源: {data_source}\n") logging.debug(f"文本参数: {text_param}\n") logging.debug(f"第三个参数: {third_param}") temp_calculator = copy.copy(claculate) groups = {} # 遍历数据源进行分组 temp_context = BCLCalcItemContext(text_param, data_source, context) for index, item in enumerate(temp_context): try: # 根据第三个参数类型获取分组键 if args[2].type == BCLVariantType.TEXT: # 文本类型直接作为键 group_key = third_param else: result = temp_calculator.evaluate_node(third_param, temp_context) if not isinstance(result, BCLVariant): raise TypeError("分组表达式计算结果必须是BCLVariant类型") group_key = str(result.value) # 添加到对应分组 if group_key not in groups: groups[group_key] = [] groups[group_key].append(item) except Exception as e: print(f"分组处理出错: {e}") # 错误项放入'error'分组 if "error" not in groups: groups["error"] = [] groups["error"].append(item) # 返回分组后的结果 return BCLVariant(groups) def sum_func(funcname: str, claculate, context: BCLContext, *args): # 检查参数数量 if len(args) != 3: raise ValueError(f"{funcname}函数需要3个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.DATASOURCE: raise ValueError(f"{funcname}函数的第一个参数必须是数据源类型") if not isinstance(args[1], BCLVariant) or args[1].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第二个参数必须是文本类型") if not isinstance(args[2], BCLVariant) or args[2].type != BCLVariantType.EXPRESSION: raise ValueError(f"{funcname}函数的第三个参数必须是表达式类型") # 获取并存储三个参数 data_source = args[0].value data_name = args[1].value expression = args[2].value logging.debug(f"call function : {funcname}\n") logging.debug(f"数据源: {data_source}\n") logging.debug(f"数据变量名称: {data_name}") logging.debug(f"表达式: {expression}\n") temp_calculator = copy.copy(claculate) # 遍历数据源并累加结果 total = 0.0 temp_context = BCLCalcItemContext(data_name, data_source, context) for index, item in enumerate(temp_context): try: result = temp_calculator.evaluate_node(expression, temp_context) # 验证结果类型必须是BCLVariant且类型为数字 if not isinstance(result, BCLVariant): raise TypeError("表达式计算结果必须是BCLVariant类型") if result.type not in (BCLVariantType.INTEGER, BCLVariantType.FLOAT): raise TypeError(f"表达式计算结果类型必须是数字,实际是{result.type}") total += float(result.value) except Exception as e: logging.error(f"表达式计算错误: {str(e)}", exc_info=True) continue return BCLVariant(total) def filter_func(funcname: str, claculate, context: BCLContext, *args): """ 过滤函数,检查参数并执行过滤操作 :param funcname: 函数名 :param args: 参数列表,必须包含3个BCLVariant参数 :return: 过滤结果 """ # 检查参数数量 if len(args) != 3: raise ValueError(f"{funcname}函数需要3个参数,但提供了{len(args)}个参数") # 检查参数类型 if not isinstance(args[0], BCLVariant) or args[0].type != BCLVariantType.DATASOURCE: raise ValueError(f"{funcname}函数的第一个参数必须是数据源类型") if not isinstance(args[1], BCLVariant) or args[1].type != BCLVariantType.TEXT: raise ValueError(f"{funcname}函数的第二个参数必须是文本类型") if not isinstance(args[2], BCLVariant) or args[2].type != BCLVariantType.EXPRESSION: raise ValueError(f"{funcname}函数的第三个参数必须是表达式类型") # 获取并存储三个参数 data_source = args[0].value data_name = args[1].value expression = args[2].value logging.debug(f"call function : {funcname}\n") logging.debug(f"数据源: {data_source}\n") logging.debug(f"数据变量名称: {data_name}\n") logging.debug(f"表达式: {expression}\n") temp_calculator = copy.copy(claculate) # 遍历数据源并应用过滤条件 filtered_results = [] temp_context = BCLCalcItemContext(data_name, data_source, context) for index, item in enumerate(temp_context): # 调用计算器的evaluate_node方法计算表达式 try: result = temp_calculator.evaluate_node(expression, temp_context) # 验证结果类型必须是BCLVariant且类型为布尔或整数 if not isinstance(result, BCLVariant): raise TypeError("表达式计算结果必须是BCLVariant类型") if result.type not in (BCLVariantType.BOOLEAN, BCLVariantType.INTEGER): raise TypeError(f"表达式计算结果类型必须是布尔或整数,实际是{result.type}") # 判别其值为true才添加到filtered_results if result.value: filtered_results.append(item) except Exception as e: logging.error(f"表达式计算错误: {str(e)}", exc_info=True) continue return BCLVariant(filtered_results) def recursion_counter(func): def wrapper(self, node, *args, **kwargs): if not hasattr(self, "_recursion_depth"): self._recursion_depth = 0 if not hasattr(self, "_total_count"): self._total_count = 0 self._total_count += 1 self._recursion_depth += 1 try: result = func(self, node, *args, **kwargs) return result finally: self._recursion_depth -= 1 return wrapper class BCLCalculator: def __init__(self): self.expressions: Dict[str, ET.Element] = {} self.functions: Dict[str, Callable] = {} self.last_error: Optional[str] = None self.loaded_scripts = [] # 存储已加载的脚本相对路径 # 注册sum函数 self.register_function("sum", sum_func) self.register_function("calc", calc_func) self.register_function("round", round_func) self.register_function("filter", filter_func) self.register_function("iif", iif_func) self.register_function("sort", sort_func) self.register_function("group", group_func) self.register_function("groupby", groupby_func) self.register_function("for", for_func) self.register_function("in", in_func) self.register_function("isEmpty", isempty_func) self.register_function("setparam", setparam_func) self.register_function("getparam", getparam_func) self.register_function("strFind", strfind_func) def __copy__(self): # 创建新实例,复制基本属性 new_instance = self.__class__() new_instance.loaded_scripts = self.loaded_scripts # 直接引用 new_instance.expressions = self.expressions # 直接引用 new_instance.functions = self.functions # 直接引用 return new_instance def load_scripts_dir(self, dir_path: str) -> bool: """ 加载目录下所有XML脚本文件 :param dir_path: 包含XML文件的目录路径 :return: 是否全部加载成功 """ if not os.path.isdir(dir_path): self.last_error = f"目录'{dir_path}'不存在或不是目录\n" return False success = True for filename in os.listdir(dir_path): if filename.lower().endswith(".xml"): xml_path = os.path.join(dir_path, filename) if not self.load_script(xml_path): success = False return success def load_script(self, xml_path: str) -> bool: """ 加载XML脚本文件并验证其结构 :param xml_path: XML文件路径 :return: 是否加载成功 """ try: # 检查文件是否存在 if not os.path.exists(xml_path): self.last_error = f"文件'{xml_path}'不存在\n" return False # 读取并清理XML文件内容 import chardet # 检测文件编码 with open(xml_path, "rb") as f: raw_data = f.read() encoding = chardet.detect(raw_data)["encoding"] # 使用检测到的编码读取文件内容,并转换为UTF-8 with open(xml_path, "r", encoding=encoding, errors="replace") as f: content = f.read() # 确保内容是UTF-8编码 if encoding.lower() != "utf-8": content = content.encode("utf-8", errors="replace").decode("utf-8") # 清理重复的Type属性 content = self._clean_xml_attributes(content) # 解析清理后的XML root = ET.fromstring(content) # 记录已加载的脚本相对路径 relative_path = os.path.relpath(xml_path, start=os.getcwd()) self.loaded_scripts.append(relative_path) # 验证根节点 - 兼容BclDocument作为根节点或在Document-DataDefs下 if root.tag == "Document": data_defs = root.find("DataDefs") if data_defs is not None: bcl_doc = data_defs.find("BclDocument") if bcl_doc is not None: root = bcl_doc else: self.last_error = "在Document/DataDefs下未找到BclDocument节点\n" return False else: self.last_error = "Document节点缺少DataDefs子节点\n" return False elif root.tag != "BclDocument": self.last_error = "根节点必须是BclDocument或Document\n" return True # 验证所有BCLExpression节点 for expr in root.findall("BCLExpression"): if not self._validate_expression_node(expr): return False name = expr.get("name") self.expressions[name] = expr return True except ET.ParseError as e: self.last_error = f"XML解析错误: {str(e)}\n" return False except Exception as e: self.last_error = f"加载脚本失败: {str(e)}\n" return False def _clean_xml_attributes(self, xml_content: str) -> str: """ 清理XML中的重复属性,保留最后一个出现的属性值 :param xml_content: XML内容字符串 :return: 清理后的XML内容 """ # 使用正则表达式匹配并清理重复的Type属性 import re # xml_content = '''''' # 正则表达式删除所有 Type="*" 属性 xml_content = re.sub(r'Type="[^"]*"', "", xml_content) return xml_content def _validate_expression_node(self, node: ET.Element) -> bool: """ 验证表达式节点结构 :param node: ELNode节点 :return: 是否验证通过 """ # 检查必须包含ELNode子节点 el_node = node.find("ELNode") if el_node is None: self.last_error = f"表达式节点'{node.get('name')}'缺少ELNode子节点" return False # 检查ELNode类型 if el_node.get("type") != "表达式序列": self.last_error = f"表达式节点'{node.get('name')}'的ELNode类型必须是'表达式序列'" return False return True def register_function(self, name: str, func: Callable) -> None: self.functions[name] = func def calculate(self, expr_name: str, context: BCLContext) -> float: self.last_error = None if expr_name not in self.expressions: self.last_error = f"表达式'{expr_name}'未找到" logging.error(f"错误: {self.last_error}") return None try: expr_node = self.expressions[expr_name] result = self._evaluate_node(expr_node, context) if result is None: logging.error(f"错误: {self.last_error}") return None return result.value except Exception as e: self.last_error = f"计算错误: {str(e)}" print(f"错误: {self.last_error}") return None def evaluate_node(self, node: ET.Element, context: BCLContext) -> BCLVariant: self.last_error = None try: result = self._evaluate_node(node, context) if result is None: logging.error(f"错误: {self.last_error}") return result except Exception as e: self.last_error = f"计算错误: {str(e)}" print(f"错误: {self.last_error}") return None def _get_variable_value(self, var_name: str, context: BCLContext) -> BCLVariant: try: var = context.get_variable_value(var_name) if var is not None: logging.debug(f"找到变量: {var_name}, 值: {var.value}, 类型: {var.type}") return var else: logging.warning(f"未找到变量: {var_name}") raise ValueError(f"变量'{var_name}'不存在") except (AttributeError, ValueError) as e: raise e @recursion_counter def _evaluate_node(self, node: ET.Element, context: BCLContext) -> BCLVariant: node_type = node.get("type") if node_type is not None: node_type = node_type.strip() indent = " " * self._recursion_depth if node_type is None: logging.debug(f"{indent} {node.tag} {node.get('name')}") else: logging.debug(f"{indent} {node.tag} {node_type} {node.get('text')}") if node_type == "变量": var_name = node.get("text").strip() return self._get_variable_value(var_name, context) elif node_type == "数值": return BCLVariant(float(node.get("text").strip())) elif node_type == "字符串": return BCLVariant(str(node.get("text") if node.get("text") else "").strip()) elif node_type == "函数": func_name = node.get("text").strip() return self._call_function(func_name, node, context) elif str_to_operator(node_type) != BCLOperatorType.UNKNOWN: return self._evaluate_logical_expression(node, context) elif node_type == "条件运算符": return self._evaluate_flow_expression(node[0], context) elif node_type == "简单计算式": return self._evaluate_expression(node, context) elif node_type == "表达式序列": # 处理表达式序列节点 result = None for child in node: result = self._evaluate_node(child, context) return result elif node_type == "参数起始符号": # 处理参数起始符号节点 result = None for child in node: result = self._evaluate_node(child, context) return result elif node.tag == "BCLExpression" or node.tag == "LeftNode" or node.tag == "RightNode": # 处理BCLExpression节点 child = node[0] if child.tag == "ELNode" and child.get("type") == "表达式序列": return self._evaluate_node(node[0], context) else: logging.warning(f"{indent} 未知的计算节点类型: {node_type}") raise ValueError(f"未知的计算节点类型: {node_type}") def _evaluate_logical_expression(self, node: ET.Element, context: BCLContext) -> BCLVariant: """ 计算逻辑表达式 :param node: XML节点 :param context: 上下文对象 :return: 逻辑运算结果 """ node_type = node.get("type").strip() operator_type = str_to_operator(node_type) left = self._evaluate_node(node.find("LeftNode"), context) if left is None: left = BCLVariant(False) if operator_type == BCLOperatorType.NOT: if not isinstance(left, BCLVariant): raise ValueError(f"NOT运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}") if left.type in [BCLVariantType.BOOLEAN, BCLVariantType.INTEGER]: return BCLVariant(not bool(left.value)) raise ValueError(f"运算符 '{node_type}' 参数类型为 {left.type} ,不支持的\n") right = self._evaluate_node(node.find("RightNode"), context) if left is None: left = BCLVariant(False) if operator_type == BCLOperatorType.AND: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'与'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type in [BCLVariantType.BOOLEAN, BCLVariantType.INTEGER] and right.type in [ BCLVariantType.BOOLEAN, BCLVariantType.INTEGER, ]: return BCLVariant(bool(left.value) and bool(right.value)) raise ValueError(f"运算符 '{node_type}' 两边参数类型分别为 {left.type} {right.type} ,不支持的\n") elif operator_type == BCLOperatorType.OR: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'或'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type in [BCLVariantType.BOOLEAN, BCLVariantType.INTEGER] and right.type in [ BCLVariantType.BOOLEAN, BCLVariantType.INTEGER, ]: return BCLVariant(bool(left.value) or bool(right.value)) raise ValueError(f"运算符 '{node_type}' 两边参数类型分别为 {left.type} {right.type} ,不支持的\n") elif operator_type == BCLOperatorType.EQUAL: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'等于'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") # 类型转换逻辑 if BCLVariantType.BOOLEAN in [left.type, right.type]: left_value = bool(left.value if left.value else "0") right_value = bool(right.value if right.value else "0") elif BCLVariantType.FLOAT in [left.type, right.type]: left_value = float(left.value if left.value else "0") right_value = float(right.value if right.value else "0") elif BCLVariantType.INTEGER in [left.type, right.type]: left_value = float(left.value if left.value else "0") right_value = float(right.value if right.value else "0") else: left_value = left.value right_value = right.value return BCLVariant(left_value == right_value) elif operator_type == BCLOperatorType.NOT_EQUAL: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'不等于'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") return BCLVariant(left.value != right.value) elif operator_type == BCLOperatorType.GREATER: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'>'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") # 支持 FLOAT 和 INTEGER 类型的比较 if left.type in [BCLVariantType.FLOAT, BCLVariantType.INTEGER] and right.type in [ BCLVariantType.FLOAT, BCLVariantType.INTEGER, ]: return BCLVariant(float(left.value if left.value else "0") > float(right.value if right.value else "0")) raise ValueError(f"运算符 '{node_type}' 两边参数类型分别为 {left.type} {right.type} ,不支持的\n") elif operator_type == BCLOperatorType.GREATER_EQUAL: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'>='运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type in [BCLVariantType.INTEGER] and right.type in [BCLVariantType.INTEGER]: return BCLVariant( float(left.value if left.value else "0") >= float(right.value if right.value else "0") ) raise ValueError(f"运算符 '{node_type}' 两边参数类型分别为 {left.type} {right.type} ,不支持的\n") elif operator_type == BCLOperatorType.LESS: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'<'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type in [BCLVariantType.INTEGER] and right.type in [BCLVariantType.INTEGER]: return BCLVariant(float(left.value if left.value else "0") < float(right.value if right.value else "0")) raise ValueError(f"运算符 '{node_type}' 两边参数类型分别为 {left.type} {right.type} ,不支持的\n") elif operator_type == BCLOperatorType.LESS_EQUAL: if not isinstance(left, BCLVariant) or not isinstance(right, BCLVariant): raise ValueError(f"'<='运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type in [BCLVariantType.INTEGER] and right.type in [BCLVariantType.INTEGER]: return BCLVariant( float(left.value if left.value else "0") <= float(right.value if right.value else "0") ) raise ValueError(f"运算符 '{node_type}' 两边参数类型分别为 {left.type} {right.type} ,不支持的\n") # 四则运算符处理 elif operator_type == BCLOperatorType.ADD: if not (isinstance(left, BCLVariant) and isinstance(right, BCLVariant)): raise ValueError(f"'加'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ) or right.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ): raise ValueError( f"'加'运算符的操作数类型必须为数值、浮点数、布尔或文本,实际为: {left.type}和{right.type}" ) return BCLVariant(float(left.value if left.value else "0") + float(right.value if right.value else "0")) elif operator_type == BCLOperatorType.SUBTRACT: if not (isinstance(left, BCLVariant) and isinstance(right, BCLVariant)): raise ValueError(f"'减'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ) or right.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ): raise ValueError( f"'减'运算符的操作数类型必须为数值、浮点数、布尔或文本,实际为: {left.type}和{right.type}" ) return BCLVariant(float(left.value if left.value else "0") - float(right.value if right.value else "0")) elif operator_type == BCLOperatorType.MULTIPLY: if not (isinstance(left, BCLVariant) and isinstance(right, BCLVariant)): raise ValueError(f"'乘'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ) or right.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ): raise ValueError( f"'乘'运算符的操作数类型必须为数值、浮点数、布尔或文本,实际为: {left.type}和{right.type}" ) try: left_val = float(left.value) if left.value and str(left.value).strip() not in ("", "None") else 0.0 right_val = float(right.value) if right.value and str(right.value).strip() not in ("", "None") else 0.0 result = left_val * right_val except (TypeError, ValueError, AttributeError): result = 0.0 return BCLVariant(result) elif operator_type == BCLOperatorType.DIVIDE: if not (isinstance(left, BCLVariant) and isinstance(right, BCLVariant)): raise ValueError(f"'除'运算符的操作数必须是BCLVariant类型,实际得到: {type(left)}和{type(right)}") if left.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ) or right.type not in ( BCLVariantType.INTEGER, BCLVariantType.FLOAT, BCLVariantType.BOOLEAN, BCLVariantType.TEXT, ): raise ValueError( f"'除'运算符的操作数类型必须为数值、浮点数、布尔或文本,实际为: {left.type}和{right.type}" ) if float(right.value) == 0: raise ZeroDivisionError("除数不能为零") return BCLVariant(float(left.value if left.value else "0") / float(right.value if right.value else "0")) raise ValueError(f"运算符 '{node_type}' 类型不支持的\n") def _evaluate_expression(self, expr_node: ET.Element, context: BCLContext) -> BCLVariant: try: # 收集所有变量值 variables = {} for var_node in expr_node: if var_node.get("type") != "变量": continue var_name = var_node.get("text") variables[var_name] = self._get_variable_value(var_name, context) # 构建表达式字符串 expr_str = expr_node.get("text") symbols = {} for var_name, var_value in variables.items(): symbols[var_name] = float("0" if not var_value.get_value() else var_value.get_value()) calculator = ExpressionCalculator() parse_success = calculator.parse_expression(expr_str) if not parse_success: logging.error(f"表达式解析失败: {calculator.get_last_error()}") raise ValueError(f"表达式解析失败: {calculator.get_last_error()}") # 执行四则运算并返回结果 result, value = calculator.evaluate(symbols) if not result: logging.error(f"表达式计算失败: {calculator.get_last_error()}") raise ValueError(f"表达式计算失败: {calculator.get_last_error()}") return BCLVariant(value) except Exception as e: raise e def _call_function(self, func_name: str, node: ET.Element, context: BCLContext) -> BCLVariant: if func_name in self.expressions: expr_node = self.expressions[func_name] result = self._evaluate_node(expr_node, context) return result if func_name not in self.functions: raise ValueError(f"函数'{func_name}'未注册\n") args = [] for child in node: if child.get("type") == "参数起始符号": current_node = child while True: # 验证子节点数量必须为1 if len(current_node) != 1: raise ValueError(f"函数'{func_name}'参数节点数量错误,预期1个实际{len(current_node)}个\n") child_node = current_node[0] # 检查是否存在type属性 if child_node.get("type") != None: break # 继续遍历子节点 current_node = child_node args.append(BCLVariant(child_node)) continue args.append(self._evaluate_node(child, context)) return self.functions[func_name](func_name, self, context, *args) def get_last_error(self) -> Optional[str]: return self.last_error def _evaluate_flow_expression(self, node: ET.Element, context: BCLContext) -> BCLVariant: """ 处理流程表达式(如ConditionNode/ConListElm/ConNode条件分支结构) :param node: ConListElm节点 :param context: 上下文对象 :return: 满足条件的ValueNode的计算结果 """ defval = None # 只处理当前ConListElm节点,不递归遍历所有ConListElm for block in node: if block.tag == "ConListElm": con_node = None value_node = None for sub in block: if sub.tag == "ConNode": con_node = sub elif sub.tag == "ValueNode": value_node = sub if con_node is not None and value_node is not None: cond_result = self._evaluate_node(con_node, context) if cond_result and bool(cond_result.value): return self._evaluate_node( value_node if value_node.get("type") != None else value_node[0], context ) elif block.tag == "DefVal": defval = block if defval is not None: return self._evaluate_node(defval, context) else: # 没有条件成立,返回0.0 return BCLVariant(0.0)