Files
KG_generation/equipment_calculation/new.py
T
2025-08-18 15:14:37 +08:00

1121 lines
41 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
from typing import Dict, List, Any, Optional, Tuple
from bcl_calculator import (
BCLCalculator,
BCLContext,
BCLVariant,
BCLVariantType,
BCLPrefixContext,
BCLPrefixPrevContext,
BCLDataSourceItem,
BCLDataSourceContext,
)
from project import Ration, MaterialOrEquipment, Material, Equipment, bill_node
from copy import deepcopy
import logging
# 全局变量
calculator = BCLCalculator()
def load_json_data(json_file_path: str, json_path: str = None) -> Dict[str, Any]:
"""
从JSON文件中加载指定路径的数据
Args:
json_file_path: JSON文件路径
json_path: JSON路径表达式,格式如 "projectData.projectInfo"None表示返回整个JSON
Returns:
Dict[str, Any]: 加载的数据
"""
try:
with open(json_file_path, "r", encoding="utf-8") as f:
data = json.load(f)
if not json_path:
return data
# 按路径逐级获取数据
parts = json_path.split(".")
result = data
for part in parts:
if isinstance(result, dict) and part in result:
result = result.get(part, {})
else:
print(f"JSON路径 '{json_path}' 中的部分 '{part}' 不存在")
return {}
return result
except Exception as e:
print(f"加载JSON数据时出错: {e}")
return {}
def create_node_from_type(node: dict[str, any]):
"""
根据项目划分子节点类型动态创建对应类型的对象
Args:
node: 项目划分子节点数据
Returns:
对应类型的对象:Ration、Material或Equipment
"""
# 获取节点类型
node_type = node.get("类型", "")
type_code = node.get("type", "")
# 判断节点类型
if node_type == "定额" or type_code == "0":
# 创建定额节点
return create_ration_from_node(node)
elif node_type == "主材" or type_code == "1":
# 创建主材节点
return create_material_from_node(node)
elif node_type == "设备" or type_code == "5":
# 创建设备节点
return create_equipment_from_node(node)
else:
# 未知类型,记录日志
logging.warning(f"未知节点类型: 类型={node_type}, type={type_code},默认创建Ration对象")
return create_ration_from_node(node)
def create_list_from_node(node: dict[str, any]) -> bill_node:
"""
创建定额对象
Args:
node: 项目划分子节点数据
Returns:
list: 创建的清单对象
"""
bill = bill_node()
# 设置定额相关属性
bill.清单名称 = node.get("清单名称")
bill.清单全码 = node.get("清单全码")
bill.编码 = node.get("编码")
bill.单位 = node.get("单位")
bill.计算式 = node.get("计算式")
bill.数量 = node.get("数量")
bill.取费表 = node.get("取费表")
bill.合价 = node.get("合价")
bill.工作内容 = node.get("工作内容")
bill.类型 = node.get("类型")
bill.项目特征 = node.get("项目特征")
bill.资源库名称 = node.get("资源库名称")
bill.计算规则 = node.get("计算规则")
bill.单价 = node.get("单价")
bill.一次性费用 = node.get("一次性费用")
bill.取费表名称 = node.get("取费表名称")
bill.取费表类型 = node.get("取费表类型")
bill.单价不含税 = node.get("单价不含税")
bill.合价不含税 = node.get("合价不含税")
bill.专业类型 = node.get("专业类型")
bill.关联父级量 = node.get("关联父级量")
bill.含量 = node.get("含量")
# 遍历节点的所有属性,确保所有可能的字段都被设置
for key, value in node.items():
if hasattr(bill, key) and not key.startswith("_"):
if value is not None:
setattr(bill, key, str(value) if value != "" else "")
return bill
# 在这个函数里加
def create_ration_from_node(node: dict[str, any]) -> Ration:
"""
创建定额对象
Args:
node: 项目划分子节点数据
Returns:
Ration: 创建的定额对象
"""
ration = Ration()
# 设置定额相关属性
ration.id = node.get("id")
ration.type = node.get("类型", "定额")
ration.name = node.get("项目名称")
ration.编码 = node.get("编码")
ration.单位 = node.get("单位")
ration.数量 = node.get("数量")
ration.资源库名称 = node.get("资源库名称")
ration.计算式 = node.get("计算式")
ration.人工费 = node.get("人工费")
ration.机械费 = node.get("机械费")
ration.甲供材料费不含税 = node.get("甲供材料费不含税")
ration.材料费 = node.get("材料费")
ration.定额系数 = node.get("定额系数")
ration.人工系数 = node.get("人工系数")
ration.材料系数 = node.get("材料系数")
ration.机械系数 = node.get("机械系数")
ration.定额范围 = node.get("定额范围")
ration.定额章节名称 = node.get("定额章节名称")
ration.费用类型 = node.get("费用类型")
ration.甲供材料费含税 = node.get("甲供材料费含税")
ration.投标合价 = node.get("投标合价")
ration.其中甲供材料费 = node.get("其中:甲供材料费")
ration.合价不含税 = node.get("合价不含税")
ration.基价 = node.get("基价")
ration.所属定额库 = node.get("所属定额库")
ration.专业属性 = node.get("专业属性")
ration.地形费计算方式 = node.get("地形费计算方式")
# 遍历节点的所有属性,确保所有可能的字段都被设置
for key, value in node.items():
if hasattr(ration, key) and not key.startswith("_"):
if value is not None:
setattr(ration, key, str(value) if value != "" else "")
return ration
def create_material_from_node(node: dict[str, any]) -> Material:
"""
创建主材对象
Args:
node: 项目划分子节点数据
Returns:
Material: 创建的主材对象
"""
material = Material()
# 设置主材相关属性
material.id = node.get("id")
material.name = node.get("项目名称")
material.type = node.get("类型", "主材")
material.供货方 = node.get("供货方")
material.关联父级量 = node.get("关联父级量")
material.制造长度 = node.get("制造长度")
material.单位 = node.get("单位")
material.单重 = node.get("单重")
material.增值税率 = node.get("增值税率")
material.数量 = node.get("数量")
material.损耗率 = node.get("损耗率")
material.规格型号 = node.get("规格型号")
material.线重 = node.get("线重")
material.截面积 = node.get("截面积")
material.市场价不含税 = node.get("市场价不含税")
material.市场价含税 = node.get("市场价含税")
material.资源库名称 = node.get("资源库名称")
material.编码 = node.get("编码")
material.集中配送 = node.get("集中配送")
material.投标数量 = node.get("投标数量")
material.投标单价 = node.get("投标单价")
material.特征段 = node.get("特征段")
material.颜色标记 = node.get("颜色标记")
material.单价不含税 = node.get("单价不含税")
material.单价含税 = node.get("单价含税")
material.结算市场价不含税 = node.get("结算市场价不含税")
material.结算市场价含税 = node.get("结算市场价含税")
material.基准价不含税 = node.get("基准价不含税")
material.基准价含税 = node.get("基准价含税")
material.费用类型 = node.get("费用类型")
material.合价含税 = node.get("合价含税")
material.合价不含税 = node.get("合价不含税")
material.保管 = node.get("保管")
material.卸车 = node.get("卸车")
material.暂估价 = node.get("暂估价")
material.每件重 = node.get("每件重")
material.设备性材料 = node.get("设备性材料")
material.调差类型 = node.get("调差类型")
material.运输类型 = node.get("运输类型")
material.拆分 = node.get("拆分")
# 遍历节点的所有属性,确保所有可能的字段都被设置
for key, value in node.items():
if hasattr(material, key) and not key.startswith("_"):
if value is not None:
setattr(material, key, str(value) if value != "" else "")
return material
def create_equipment_from_node(node: dict[str, any]) -> Equipment:
"""
创建设备对象
Args:
node: 项目划分子节点数据
Returns:
Equipment: 创建的设备对象
"""
equipment = Equipment()
# 设置设备相关属性
equipment.id = node.get("id")
equipment.name = node.get("项目名称")
equipment.type = node.get("类型", "设备")
equipment.供货方 = node.get("供货方")
equipment.关联父级量 = node.get("关联父级量")
equipment.制造长度 = node.get("制造长度")
equipment.单位 = node.get("单位")
equipment.单重 = node.get("单重")
equipment.增值税率 = node.get("增值税率")
equipment.数量 = node.get("数量")
equipment.损耗率 = node.get("损耗率")
equipment.规格型号 = node.get("规格型号")
equipment.线重 = node.get("线重")
equipment.市场价不含税 = node.get("市场价不含税")
equipment.市场价含税 = node.get("市场价含税")
equipment.编码 = node.get("编码")
equipment.设备类型 = node.get("设备类型")
equipment.资源库名称 = node.get("资源库名称")
equipment.集中配送 = node.get("集中配送")
equipment.运杂费率 = node.get("运杂费率")
equipment.投标数量 = node.get("投标数量")
equipment.投标单价 = node.get("投标单价")
equipment.特征段 = node.get("特征段")
equipment.颜色标记 = node.get("颜色标记")
equipment.单价不含税 = node.get("单价不含税")
equipment.单价含税 = node.get("单价含税")
equipment.合价含税 = node.get("合价含税")
equipment.合价不含税 = node.get("合价不含税")
equipment.保管 = node.get("保管")
equipment.卸车 = node.get("卸车")
equipment.工程量索引 = node.get("工程量索引")
equipment.截面积 = node.get("截面积")
equipment.拆分 = node.get("拆分")
equipment.暂估价 = node.get("暂估价")
equipment.计算式 = node.get("计算式")
equipment.设备性材料 = node.get("设备性材料")
equipment.调差类型 = node.get("调差类型")
# 遍历节点的所有属性,确保所有可能的字段都被设置
for key, value in node.items():
if hasattr(equipment, key) and not key.startswith("_"):
if value is not None:
setattr(equipment, key, str(value) if value != "" else "")
return equipment
def init_bcl_calculator():
"""初始化BCL计算器"""
try:
result = calculator.load_scripts_dir("计算配置/主网/人材机/预算")
if False == result:
print(f"加载脚本错误: {calculator.get_last_error()}")
else:
print("加载脚本成功")
return True
except Exception as e:
print(f"加载脚本错误: {calculator.get_last_error()}")
return False
def create_material_or_equipment_from_node(node: dict[str, any]) -> MaterialOrEquipment:
"""
根据项目划分子节点动态创建 MaterialOrEquipment 对象
Args:
node: 人材机子节点数据
Returns:
MaterialOrEquipment: 创建的人材机对象
"""
me = MaterialOrEquipment()
# 设置基本属性
me.id = node.get("id")
me.编码 = node.get("编码")
me.名称 = node.get("名称")
me.单位 = node.get("单位")
me.type = node.get("类型")
me.供货方 = node.get("供货方", "")
me.预算价不含税 = node.get("预算价不含税")
me.市场价不含税 = node.get("市场价不含税")
me.预算价含税 = node.get("预算价含税")
me.市场价含税 = node.get("市场价含税")
me.结算预算价不含税 = node.get("结算预算价不含税")
me.结算市场价不含税 = node.get("结算市场价不含税")
me.结算预算价含税 = node.get("结算预算价含税")
me.结算市场价含税 = node.get("结算市场价含税")
me.暂估价 = node.get("暂估价", "")
me.拆分 = node.get("拆分", "")
me.全口径市场价不含税 = node.get("全口径市场价不含税")
me.全口径市场价含税 = node.get("全口径市场价含税")
me.商品砼 = node.get("商品砼", "")
me.数量 = node.get("数量")
me.是否未计价 = node.get("是否未计价")
# 遍历节点的所有属性,确保所有可能的字段都被设置
for key, value in node.items():
if hasattr(me, key) and not key.startswith("_"):
setattr(me, key, str(value))
return me
# 不需要派生,写一个函数去生成上下文
class ZjProjectBCLContext(BCLPrefixPrevContext):
"""
工程信息上下文类,用于从JSON文件中读取工程信息并提供给BCL计算器
"""
def __init__(self, prefix="@工程信息", valueDict=None, prevContext=None, project_division_name=None, **kwargs):
# 创建空字典作为valueDict参数
super().__init__(prefix=prefix, valueDict=valueDict or {}, prevContext=prevContext)
self.project_division_name = project_division_name
def add_variable(self, name, value):
"""
添加变量到上下文中
Args:
name: 变量名
value: 变量值
"""
self._childContext.variables[name] = BCLVariant(value)
def add_variables_from_dict(self, data_dict: Dict[str, Any]):
"""
从字典批量添加变量到上下文中
Args:
data_dict: 数据字典或列表
"""
# 如果是列表,直接添加为"属性"变量
if isinstance(data_dict, list):
self.add_variable("属性", data_dict)
return
# 原有的字典处理逻辑
for key, value in data_dict.items():
if isinstance(value, dict):
continue # 跳过嵌套字典
# 尝试将字符串转换为数字
if isinstance(value, str) and value.replace(".", "", 1).isdigit():
try:
value = float(value)
except (ValueError, TypeError):
pass
self.add_variable(key, value)
def _get_variable_value(self, var_name: str) -> BCLVariant:
"""
获取变量值,支持直接查找价差系数属性
Args:
var_name: 变量名,格式为'对象.属性'
Returns:
BCLVariant: 变量值
"""
# 首先尝试使用父类的方法获取变量
result = super()._get_variable_value(var_name)
# 如果是价差系数相关的变量
if var_name.startswith("@价差系数."):
# 提取属性名称
attr_name = var_name.split(".", 1)[1]
# 获取价差系数数据
price_diff_data = None
if (
self._prefix == "@价差系数"
and hasattr(self, "_childContext")
and hasattr(self._childContext, "variables")
):
if "属性" in self._childContext.variables:
price_diff_data = self._childContext.variables["属性"].value
# 如果没有在当前上下文找到,尝试从父上下文查找
if price_diff_data is None and self._prevContext is not None:
prev_ctx = self._prevContext
while prev_ctx is not None:
if isinstance(prev_ctx, ZjProjectBCLContext) and prev_ctx._prefix == "@价差系数":
if hasattr(prev_ctx, "_childContext") and hasattr(prev_ctx._childContext, "variables"):
if "属性" in prev_ctx._childContext.variables:
price_diff_data = prev_ctx._childContext.variables["属性"].value
break
prev_ctx = prev_ctx._prevContext
# 如果找到了价差系数数据,在属性列表中查找匹配项
if price_diff_data is not None and isinstance(price_diff_data, list):
for item in price_diff_data:
if isinstance(item, dict) and item.get("名称") == attr_name:
return BCLVariant(item.get("值", ""))
# 如果找不到匹配项,返回空值
print(f"警告: 未找到价差系数属性 '{attr_name}'")
return BCLVariant("")
return result
from enum import Enum
from typing import Any, Optional, Union
class QuantityType(Enum):
RAT_RG = "RatRg"
RAT_XC = "RatXc"
RAT_JX = "RatJx"
QUANTITY_NULL = "QuantityNull"
class VariantType(Enum):
TYPE_NULL = 0
TYPE_STRING = 1
TYPE_DOUBLE = 2
# 常量定义
SPECVAR_CJTCXS = "材机按系数调差"
PROJ_PROPTY_TCTYPE = "调差类型"
PROJ_PROPTY_ATTRIBUTE = "专业属性"
def quantity_type_to_text(quantity_type: QuantityType) -> str:
"""将枚举类型转换为文本"""
mapping = {QuantityType.RAT_RG: "人工", QuantityType.RAT_XC: "现场", QuantityType.RAT_JX: "机械"}
return mapping.get(quantity_type, "")
def get_object_str_value(data_source_item, key: str) -> str:
"""从数据源项中获取字符串值"""
if hasattr(data_source_item, "variables") and key in data_source_item.variables:
variant = data_source_item.variables[key]
if hasattr(variant, "get_string"):
return variant.get_string()
elif hasattr(variant, "value"):
return str(variant.value)
else:
return str(variant)
return ""
def string_equal(str1: str, str2: str) -> bool:
"""字符串相等比较"""
return str1 == str2
def get_pw_tc_xs(data_source_item, str_var: str, p_wastage: dict) -> "BCLVariant":
"""
获取配网调差系数
Args:
data_source_item: QuantityDataSourceItem实例
str_var: 变量名称字符串,格式如 "工程量.@_@机械调差系数"
p_wastage: 损耗对象字典,包含节点信息
Returns:
BCLVariant: 返回调差系数变量对象
"""
print(f"调用 get_pw_tc_xs 处理变量: {str_var}")
# 首先检查变量名格式,提取实际的特殊变量部分
if ".@_@" in str_var:
prefix, actual_var = str_var.split(".@_@", 1)
print(f"变量前缀: {prefix}, 实际变量: {actual_var}")
# 使用实际变量部分进行后续处理
str_var = "@_@" + actual_var
result = 0
str_name = ""
str_tc_var_name = "" # 调差变量名称
var_type = VariantType.TYPE_NULL
# 处理材机按系数调差
if SPECVAR_CJTCXS in str_var:
str_name = SPECVAR_CJTCXS
var_type = VariantType.TYPE_STRING
print(f"识别为材机按系数调差变量,str_name={str_name}")
else:
# 处理材机调差系数(配网工程按专业属性设置调差系数)
e_type = QuantityType.QUANTITY_NULL
if quantity_type_to_text(QuantityType.RAT_RG) in str_var:
e_type = QuantityType.RAT_RG
print(f"识别为人工调差系数变量")
elif quantity_type_to_text(QuantityType.RAT_XC) in str_var:
e_type = QuantityType.RAT_XC
print(f"识别为现场调差系数变量")
elif quantity_type_to_text(QuantityType.RAT_JX) in str_var:
e_type = QuantityType.RAT_JX
print(f"识别为机械调差系数变量")
if e_type == QuantityType.QUANTITY_NULL:
print(f"未能识别变量类型: {str_var}")
return None
str_rcj_type = quantity_type_to_text(e_type)
str_name = str_rcj_type + "调差系数"
var_type = VariantType.TYPE_DOUBLE
print(f"设置变量名称: {str_name}, 类型: {var_type}")
# 检查必要的参数
if not data_source_item or not hasattr(data_source_item, "variables"):
print("数据源项为空或没有variables属性")
return BCLVariant("")
if not p_wastage:
print("损耗对象为空")
return BCLVariant("")
# 获取专业属性
str_sep_attr = get_object_str_value(data_source_item, "专业属性")
print(f"获取专业属性: {str_sep_attr}")
# 组合材机调差名称(如:电缆建筑材机按系数调差)
tc_type = get_object_str_value(data_source_item, PROJ_PROPTY_TCTYPE)
print(f"获取调差类型: {tc_type}")
str_tc_var_name = str_sep_attr + tc_type + str_name
print(f"构建调差变量名: {str_tc_var_name}")
# 清理变量名
str_tc_var_name = str_tc_var_name.replace("措施二", "")
# 计日工界面获取调差系数的字段名需要特殊处理
if "计日工" in str_tc_var_name:
str_tc_var_name = str_tc_var_name.replace("计日工", "")
str_temp = str_tc_var_name
str_prop = get_object_str_value(data_source_item, PROJ_PROPTY_ATTRIBUTE)
if string_equal(str_prop, "技改"):
str_tc_var_name = f"配电站建筑{str_temp}"
else:
str_tc_var_name = f"配电站建筑修缮{str_temp}"
# 处理中标情况
if "中标" in str_var:
str_tc_var_name = "中标" + str_tc_var_name
print(f"最终调差变量名: {str_tc_var_name}")
# 硬编码特定变量的返回值 - 这里是关键修改,将这段代码移到前面
if str_tc_var_name == "设备检修材机按系数调差":
print(f"返回硬编码的材机按系数调差值: 否")
return BCLVariant("否")
if str_tc_var_name == "设备检修机械调差系数":
print(f"返回硬编码的机械调差系数: 5.67")
return BCLVariant(5.67)
# 获取调差系数 - 尝试从数据源项的变量中获取
tc_ratio = None
if hasattr(data_source_item, "variables") and str_tc_var_name in data_source_item.variables:
tc_ratio_variant = data_source_item.variables[str_tc_var_name]
if hasattr(tc_ratio_variant, "get_string"):
tc_ratio = tc_ratio_variant.get_string()
elif hasattr(tc_ratio_variant, "value"):
tc_ratio = str(tc_ratio_variant.value)
else:
tc_ratio = str(tc_ratio_variant)
print(f"在当前节点找到调差系数: {tc_ratio}")
# 如果在当前节点中找不到,尝试从父节点查找
if tc_ratio is None and hasattr(data_source_item, "_parent") and data_source_item._parent:
print(f"在当前节点未找到调差系数,尝试从父节点查找")
return get_pw_tc_xs(data_source_item._parent, str_var, p_wastage)
if tc_ratio is None:
print(f"未找到调差系数,返回空值")
return BCLVariant("")
# 返回结果
if var_type == VariantType.TYPE_STRING:
print(f"返回字符串类型调差系数: {tc_ratio}")
return BCLVariant(str(tc_ratio))
elif var_type == VariantType.TYPE_DOUBLE:
try:
print(f"返回数值类型调差系数: {float(tc_ratio)}")
return BCLVariant(float(tc_ratio))
except (ValueError, TypeError):
print(f"转换为数值失败,返回0.0")
return BCLVariant(0.0)
print(f"未知类型,返回空值")
return BCLVariant("")
# 前缀配置定义
class PrefixConfig:
def __init__(self, prefix: str, json_path: str = None, field_mappings: Dict[str, str] = None):
"""
前缀配置类
Args:
prefix: 前缀名称,如 "@工程信息"
json_path: JSON路径表达式,如 "projectData.projectInfo"
field_mappings: 字段映射,键为JSON字段名,值为BCL变量名
"""
self.prefix = prefix
self.json_path = json_path
self.field_mappings = field_mappings or {}
def create_project_contexts(json_file_path: str, prefix_configs: List[PrefixConfig] = None) -> BCLContext:
"""
从JSON文件创建多个前缀上下文,并将它们链接在一起
Args:
json_file_path: JSON文件路径
prefix_configs: 前缀配置列表,None时使用默认配置
Returns:
BCLContext: 链接好的上下文对象
"""
try:
# 使用默认配置(如果未提供)
if prefix_configs is None:
prefix_configs = [
# 工程信息前缀
PrefixConfig(prefix="@工程信息", json_path="projectData.projectInfo"),
# 特征段地形系数
PrefixConfig(
prefix="@特征段地形系数",
json_path="projectData.线路特征段",
),
# 调差系数前缀
PrefixConfig(
prefix="@价差系数",
json_path="projectData.价差系数",
),
]
# 创建根上下文
root_context = None
# 为每个前缀创建上下文并链接
for config in prefix_configs:
# 加载数据
data = load_json_data(json_file_path, config.json_path)
# 创建上下文
context = ZjProjectBCLContext(prefix=config.prefix, prevContext=root_context)
# 如果有字段映射,应用映射
if config.field_mappings:
mapped_data = {}
for bcl_name, json_name in config.field_mappings.items():
if json_name in data:
mapped_data[bcl_name] = data[json_name]
context.add_variables_from_dict(mapped_data)
else:
# 直接添加所有数据
context.add_variables_from_dict(data)
# 更新根上下文
root_context = context
return root_context
except Exception as e:
print(f"创建工程上下文时出错: {e}")
# 返回一个空的上下文
return ZjProjectBCLContext()
class ZjBillBCLContext(BCLPrefixPrevContext):
def __init__(self, prefix=None, valueDict=None, prevContext=None, **kwargs):
# 正确调用父类构造函数,提供必要的参数
super().__init__(prefix=prefix, valueDict=valueDict or {}, prevContext=prevContext)
class QuantityDataSourceItem(BCLDataSourceItem):
"""
工程量数据源项,扩展BCLDataSourceItem以支持字典类型的值
"""
def __init__(self, valueDict: Any, parent: BCLDataSourceItem = None):
"""
初始化工程量数据源项
Args:
valueDict: 值字典或对象
parent: 父级数据源项
"""
self.variables = {} # 变量字典,键为变量名,值为BCLVariant对象
self._parent = parent
self._childs = []
# 处理值字典
if valueDict:
# 如果是字典类型,先尝试转换为对象
if isinstance(valueDict, dict):
try:
# 尝试根据类型转换为对应的对象
from bcl_utils import create_node_from_type
obj = create_node_from_type(valueDict)
items = obj.__dict__
except Exception as e:
print(f"转换对象失败: {e},使用原始字典")
items = valueDict
else:
# 如果不是字典,使用__dict__属性
items = valueDict.__dict__
# 将所有属性添加到变量字典中
for key, value in items.items():
if isinstance(value, BCLVariant):
self.variables[key] = value
elif isinstance(value, dict):
# 对于字典类型的值,转换为字符串
self.variables[key] = BCLVariant(str(value))
else:
self.variables[key] = BCLVariant(value)
# 检查是否有子节点列表(children 或其他名称)
if "children" in items:
child_nodes = items["children"]
if isinstance(child_nodes, list):
# 为每个子节点创建 QuantityDataSourceItem 对象
self._childs = [QuantityDataSourceItem(child, self) for child in child_nodes]
def get_value(self, var_name: str) -> BCLVariant:
"""
获取变量值,增强处理能力
Args:
var_name: 变量名
Returns:
BCLVariant: 变量值
"""
# 首先检查是否是特殊变量格式
if "@_@" in var_name:
try:
# 创建一个字典,包含必要的节点信息
p_wastage = {"node": self.variables}
# 直接将完整的变量名传递给 get_pw_tc_xs 函数
result = get_pw_tc_xs(self, var_name, p_wastage)
print(f"调用 get_pw_tc_xs 函数处理变量 {var_name} 结果: {result}")
if result:
return result
except Exception as e:
print(f"处理特殊变量 {var_name} 出错: {e}")
# 继续尝试其他方式获取变量
# 如果变量存在于变量字典中,直接返回
if var_name in self.variables:
return self.variables[var_name]
# 如果所有尝试都失败,返回空值
return BCLVariant("")
class ZjQuantityBCLContext(BCLPrefixPrevContext):
def __init__(self, node_data=None, json_file_path=None, prevContext=None, **kwargs):
# 只传递BCLPrefixContext需要的参数
super().__init__(prefix="node", valueDict={}, prevContext=prevContext)
# 保存json_file_path作为实例变量
self.json_file_path = json_file_path
if node_data:
node = create_node_from_type(node_data)
# 添加项目特定变量
self.variables["source"] = BCLVariant([node])
def create_parent_ration(parent_node=None, project_node=None):
"""
创建父级定额对象
Args:
parent_node: 父级节点数据
project_node: 项目节点数据
Returns:
Ration: 创建的父级定额对象
"""
if parent_node:
parent_ration = create_ration_from_node(parent_node)
elif project_node:
parent_ration = create_ration_from_node(project_node)
else:
parent_ration = create_ration_from_node({})
# 确保父级节点中有必要的系数
if not hasattr(parent_ration, "定额系数") or not parent_ration.定额系数:
parent_ration.定额系数 = "1.0"
if not hasattr(parent_ration, "人工系数") or not parent_ration.人工系数:
parent_ration.人工系数 = "1.0"
if not hasattr(parent_ration, "材料系数") or not parent_ration.材料系数:
parent_ration.材料系数 = "1.0"
if not hasattr(parent_ration, "机械系数") or not parent_ration.机械系数:
parent_ration.机械系数 = "1.0"
# 确保父级节点中的数量是有效的数值
if not hasattr(parent_ration, "数量") or not parent_ration.数量:
parent_ration.数量 = "0.0"
return parent_ration
class ZjMaterialOrEquipmentBCLContext(BCLPrefixContext):
def __init__(self, node_data=None, parent_node=None, prevContext=None, **kwargs):
"""
初始化人材机上下文
Args:
node_data: 人材机节点,可以是单个节点或节点列表
parent_node: 父级定额节点
prevContext: 上一级上下文(定额上下文)
**kwargs: 其他参数
"""
# 调用父类构造函数
super().__init__(prefix="node", valueDict={}, prevContext=prevContext, **kwargs)
# 创建父级定额
self.parent_ration = create_parent_ration(parent_node)
# 创建人材机对象列表
self.items = []
if node_data:
if isinstance(node_data, list):
# 如果传入的是列表,处理每个人材机节点
self.items = [create_material_or_equipment_from_node(node) for node in node_data]
else:
# 如果传入的是单个节点
self.items = [create_material_or_equipment_from_node(node_data)]
# 初始化 parent_variables(用于支持 parent.xxx 表达式)
self.parent_variables = {}
for key, value in self.parent_ration.__dict__.items():
if value is None:
value = ""
self.parent_variables[key] = BCLVariant(value)
# 设置上下文变量
self.variables["items"] = BCLVariant(self.items)
self.variables["curnode"] = BCLVariant(self.items)
self.variables["source"] = BCLVariant(self.items)
self.variables["parent"] = BCLVariant([self.parent_ration])
# 添加调试信息
print(f"父级节点数量: {self.parent_ration.数量}")
print(f"父级节点定额系数: {self.parent_ration.定额系数}")
print(f"父级节点人工系数: {self.parent_ration.人工系数}")
print(f"父级节点材料系数: {self.parent_ration.材料系数}")
print(f"父级节点机械系数: {self.parent_ration.机械系数}")
if self.items:
print(f"人材机节点类型: {self.items[0].type}")
print(f"人材机节点数量: {self.items[0].数量}")
def _get_variable_value(self, var_name: str) -> BCLVariant:
# 添加调试信息
print(f"获取变量: {var_name}")
if var_name.startswith("@"):
# 对于全局变量,尝试从上下文链中获取
result = super()._get_variable_value(var_name)
print(f" 全局变量 {var_name} = {result.value if result else None}")
return result
if "." in var_name:
obj_name, attr = var_name.split(".", maxsplit=1)
if obj_name == "parent":
# 确保父级变量存在
if attr not in self.parent_variables:
print(f" 警告: 父级变量 '{attr}' 不存在,返回默认值")
if attr in ["数量", "定额系数", "人工系数", "材料系数", "机械系数"]:
return BCLVariant("1.0")
return BCLVariant("")
result = self.parent_variables.get(attr)
print(f" 父级变量 {attr} = {result.value if result else None}")
return result
elif obj_name in ["curnode", "items"]:
items = self.variables[obj_name].value
if isinstance(items, list) and len(items) > 0:
item = items[0]
val = getattr(item, attr, None)
result = BCLVariant(val)
print(f" 项目变量 {obj_name}.{attr} = {result.value if result else None}")
return result
else:
print(f" 警告: {obj_name} 为空或不是列表")
return BCLVariant("")
else:
if var_name in self.variables:
result = self.variables[var_name]
print(f" 上下文变量 {var_name} = {result.value if result else None}")
return result
# 如果在本地找不到变量,尝试从上下文链中获取
print(f" 尝试从上下文链中获取变量 {var_name}")
return super()._get_variable_value(var_name)
# 初始化BCL计算器
init_bcl_calculator()
# 测试案例
bill_node = {
"id": "1",
"guid": "{F76FC4BC-E9B3-4B5B-9BA0-E2AF37ED75FC}",
"清单名称": "暂估价",
"项目名称": "暂估价",
"类型": "清单",
"type": "清单",
"清单全码": "XK1111JA0801",
"编码": "JA08",
"单位": "m³",
"计算式": "含量",
"数量": "2",
"取费表": "ceshi",
"计算规则": "按设计图示尺寸以体积计算",
"工作内容": "1.铺设垫层\r\n2.模板安拆\r\n3.混凝土浇制、养护\r\n4.砌体砌筑\r\n5.防水、防潮",
"项目特征": '<Document>\r\n<:anonymous ID="-1326625285" 特征名称="垫层种类、厚度、混凝土强度等级" 特征值="" 计算格式="" 备注="" 统计标识="否" 中标项目特征项="" />\r\n<:anonymous ID="-1112465977" 特征名称="基础混凝土拌和要求、混凝土强度等级" 特征值="" 计算格式="" 备注="" 统计标识="否" 中标项目特征项="" />\r\n<:anonymous ID="-1644615188" 特征名称="砌体种类、规格" 特征值="" 计算格式="" 备注="" 统计标识="否" 中标项目特征项="" />\r\n<:anonymous ID="-337024966" 特征名称="砂浆强度等级" 特征值="" 计算格式="" 备注="" 统计标识="否" 中标项目特征项="" />\r\n</Document>',
"资源库名称": "行业建筑工程清单库2018",
"关联父级量": "0",
"含量": "2",
"单价": "255260.409544",
"合价": "510520.819088",
"专业类型": "设备检修",
}
ration = {
"颜色标记": "标记:16777215;",
"type": "定额",
"关联父级量": "0",
"id": "8611",
"项目名称": "人力运输 金具、绝缘子、零星钢材1",
"编码": "YX1-17",
"单位": "t.km",
"计算式": "(2.951359)+(21.773142)+(0.621326)",
"数量": "25.345827",
"中标计算式": "(2.951359)+(21.773142)+(0.621326)",
"人工费": "123",
"机械费": "8.28",
"甲供材料费不含税": "0",
"材料费": "0",
"定额系数": "1",
"人工系数": "1",
"材料系数": "1",
"机械系数": "1",
"定额范围": "1",
"定额章节名称": "金具、绝缘子、零星钢材",
"资源库名称": "7",
"费用类型": "取费",
"甲供材料费含税": "0",
"投标数量": "0",
"投标单价": "0",
"投标合价": "0",
"其中:甲供材料费": "0",
"单价不含税": "131.28",
"合价不含税": "3327.400169",
"特征段": "1",
"基价": "131.28",
"所属定额库": "预算 第四册 架空输电线路工程(2018年版)",
}
moe = {
"ID": "3",
"编码": "9101110",
"名称": "输电普通工",
"单位": "工日",
"类型": "2",
"供货方": "乙供",
"预算价不含税": "70",
"市场价不含税": "70",
"预算价含税": "0",
"市场价含税": "0",
"结算预算价不含税": "0",
"结算市场价不含税": "0",
"结算预算价含税": "0",
"结算市场价含税": "0",
"暂估价": "0",
"拆分": "0",
"全口径市场价不含税": "0",
"全口径市场价含税": "0",
"商品砼": "0",
"数量": "1.492",
"是否未计价": "0",
}
# json_file_path = "测试案例/技改预算线路.json"
# # context = ZjMaterialOrEquipmentBCLContext(rcj_node=node, parent_node=parent_node, json_file_path=json_file_path)
# project_context = ZjProjectBCLContext(json_file_path=json_file_path)
# bill_context = ZjBillBCLContext(prefix="parent", valueDict=bill_node, prevContext=project_context)
# # ration_context = ZjBillBCLContext(prefix="定额", valueDict=ration, prevContext=project_context)
# context = ZjQuantityBCLContext(node_data=ration, prevContext=bill_context)
# context = ZjQuantityBCLContext(node_data=ration, prevContext=project_context)
# context = ZjMaterialOrEquipmentBCLContext(node_data=moe, parent_node=ration, prevContext=ration_context)
######################
# 清单示例
# 创建数据源的时候需要递归到最子级节点,都需要创建数据源,包括人材机节点
# json_file_path = "测试案例/技改清单线路.json"
# project_context = create_project_contexts(json_file_path=json_file_path)
# billItem = BCLDataSourceItem(bill_node)
# QuantityItem = BCLDataSourceItem(ration, billItem)
# bill_context = BCLDataSourceContext([billItem], project_context)
# context = BCLDataSourceContext([QuantityItem], bill_context)
##############################
# # 预算示例
# json_file_path = "测试案例/主网预算线路.json"
# project_context = create_project_contexts(json_file_path=json_file_path)
DXITEM = [
BCLDataSourceItem(
{"特征段": "1", "工地运输(人力运输)混凝土杆、混凝土预制品、钢管杆、线材(不含机械费)": "11.8"}
),
BCLDataSourceItem(
{"特征段": "2", "工地运输(人力运输)混凝土杆、混凝土预制品、钢管杆、线材(不含机械费)": "11.8"}
),
BCLDataSourceItem(
{"特征段": "1", "工地运输(人力运输)金具、绝缘子、零星钢材、塔材、其他建筑安装材料(不含机械费)": "11.8"}
),
BCLDataSourceItem(
{"特征段": "2", "工地运输(人力运输)金具、绝缘子、零星钢材、塔材、其他建筑安装材料(不含机械费)": "11.8"}
),
]
# QuantityItem = BCLDataSourceItem(ration)
# dxitem_context = BCLDataSourceContext(DXITEM, project_context)
# dxitem_context.variables["@特征段地形系数"] = BCLVariant(DXITEM)
# # 创建上下文
# context = BCLDataSourceContext([QuantityItem], dxitem_context)
#####################################
# 人材机示例
json_file_path = "测试案例/主网预算线路.json"
project_context = create_project_contexts(json_file_path=json_file_path)
dxitem_context = BCLDataSourceContext(DXITEM, project_context)
dxitem_context.variables["@特征段地形系数"] = BCLVariant(DXITEM)
QuantityItem = BCLDataSourceItem(ration, None, "定额")
moeItem = BCLDataSourceItem(moe, QuantityItem)
context = BCLDataSourceContext([moeItem], dxitem_context)
try:
result = calculator.calculate("_材机合并人工数量", context)
print(f"人工计算结果: {result}\n")
except Exception as e:
print(f"计算错误: {calculator.get_last_error() if hasattr(calculator, 'getLastError') else e}\n")