clac_check_tools.py 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. import json
  2. import logging
  3. from typing import Annotated
  4. from langchain.tools import tool, InjectedState
  5. # 外部子函数导入
  6. # 全局日志配置
  7. logging.basicConfig(level=logging.INFO, format="【%(name)s】%(message)s")
  8. logger = logging.getLogger("VentCheckTool")
  9. # ===================== 公共工具函数(全局复用,修复原有语法错误) =====================
  10. def get_safe_value(value, default=0):
  11. """安全取值,兼容None"""
  12. if value is None:
  13. return default
  14. return value
  15. def get_safe_bool(value, default=False):
  16. """安全获取布尔值"""
  17. if value is None:
  18. return default
  19. return value
  20. def append_report_line(report_list: list, text: str = ""):
  21. """统一拼接报告行(修复原语法错误)"""
  22. report_list.append(text)
  23. # 温度 -> 风速映射(公共逻辑)
  24. def resolve_velocity_by_temperature(temp: float) -> float:
  25. if temp <= 20:
  26. return 0.8
  27. elif temp <= 23:
  28. return 1.0
  29. elif temp <= 26:
  30. return 1.2
  31. elif temp <= 28:
  32. return 1.5
  33. else:
  34. return 1.8
  35. # 汇总打印公共方法
  36. def format_working_face_summary(results):
  37. """工作面汇总报告"""
  38. lines = []
  39. append_report_line(lines, f"{'=' * 90}")
  40. append_report_line(lines, " " * 35 + "验算汇总报告")
  41. append_report_line(lines, f"{'=' * 90}")
  42. append_report_line(lines, "工作面列表:")
  43. for r in results:
  44. status = "✓" if r["all_valid"] else "✗"
  45. append_report_line(lines, f" {status} {r['face_name']}:{'验算通过' if r['all_valid'] else '验算不通过'}")
  46. all_pass = all(r["all_valid"] for r in results)
  47. append_report_line(lines, f"{'─' * 90}")
  48. append_report_line(lines, f"总体结论:{'✓ 全部验算通过' if all_pass else '✗ 存在验算不通过,请检查并修正'}")
  49. append_report_line(lines, f"{'=' * 90}")
  50. return "".join(lines)
  51. def format_tunneling_summary(results):
  52. """掘进工作面汇总"""
  53. lines = []
  54. append_report_line(lines, "掘进工作面列表:")
  55. for r in results:
  56. status = "✓" if r["all_valid"] else "✗"
  57. append_report_line(lines, f" {status} {r['face_name']}:{'验算通过' if r['all_valid'] else '验算不通过'}")
  58. all_pass = all(r["all_valid"] for r in results)
  59. append_report_line(lines, f"总体结论:{'✓ 全部验算通过' if all_pass else '✗ 存在验算不通过,请检查并修正'}")
  60. return "".join(lines)
  61. def format_chamber_summary(results):
  62. """硐室汇总"""
  63. lines = []
  64. append_report_line(lines, "硐室列表:")
  65. for r in results:
  66. status = "✓" if r["all_valid"] else "✗"
  67. append_report_line(lines, f" {status} {r['name']}{'(缺少Shf参数)' if r.get('missing_shf', False) else ''}:{'验算通过' if r['all_valid'] else '验算不通过'}")
  68. all_pass = all(r["all_valid"] for r in results)
  69. append_report_line(lines, f"总体结论:{'✓ 全部验算通过' if all_pass else '✗ 存在验算不通过'}")
  70. return "".join(lines)
  71. def format_other_points_summary(results):
  72. """其他用风地点汇总"""
  73. lines = []
  74. append_report_line(lines, "其他用风地点列表:")
  75. for r in results:
  76. status = "✓" if r["all_valid"] else "✗"
  77. append_report_line(lines, f" {status} {r['name']}:{'验算通过' if r['all_valid'] else '验算不通过'}")
  78. all_pass = all(r["all_valid"] for r in results)
  79. append_report_line(lines, f"总体结论:{'✓ 全部验算通过' if all_pass else '✗ 存在验算不通过,请检查并修正'}")
  80. return "".join(lines)
  81. # ===================== 1. 采煤工作面验算工具 =====================
  82. @tool
  83. def verify_coal_face_ventilation(
  84. state: Annotated[dict, InjectedState]
  85. ) -> str:
  86. """
  87. 验算采煤工作面需风量计算是否正确
  88. 状态由框架自动注入,包含全局配风计划数据
  89. """
  90. # 从注入的状态读取配风数据(全局统一字段:vent_plan_data)
  91. vent_plan = state.get("vent_plan_data", {})
  92. coal_faces = vent_plan.get("coal_faces", [])
  93. if not coal_faces:
  94. return "❌ 未获取到采煤工作面数据,无法验算"
  95. report_lines = []
  96. append_report_line(report_lines, "验算采煤工作面需风量计算是否正确")
  97. results = []
  98. normal_results = {}
  99. # 处理正常工作面
  100. for face in coal_faces:
  101. if not face.get("is_standby", False):
  102. res, report = _verify_normal_working_face(face, resolve_velocity_by_temperature)
  103. append_report_line(report_lines, report)
  104. results.append(res)
  105. normal_results[face["face_name"]] = res
  106. # 处理备用工作面
  107. for face in coal_faces:
  108. if face.get("is_standby", False):
  109. res, report = _verify_standby_working_face(face, normal_results)
  110. append_report_line(report_lines, report)
  111. results.append(res)
  112. # 汇总报告
  113. append_report_line(report_lines, format_working_face_summary(results))
  114. return "".join(report_lines)
  115. def _verify_normal_working_face(face, get_vcf_func):
  116. """单条正常采煤工作面验算(内部私有函数)"""
  117. face_name = get_safe_value(face.get("face_name"), "Unknown")
  118. lines = []
  119. append_report_line(lines, f"【工作面:{face_name}】")
  120. append_report_line(lines, " 类型:正常生产工作面")
  121. # 1. 瓦斯涌出量风量
  122. qcg = get_safe_value(face.get("qcg"), 0)
  123. kcg = get_safe_value(face.get("kcg"), 0)
  124. input_qcfg = get_safe_value(face.get("Qcfg"), 0)
  125. append_report_line(lines, "【验算一】按瓦斯涌出量计算风量 (Qcfg)")
  126. append_report_line(lines, " 计算公式:Qcfg = 100 × qcg × kcg")
  127. append_report_line(lines, f" 参数取值:qcg = {qcg} m³/min, kcg = {kcg}")
  128. qcfg_valid = True
  129. correct_qcfg = input_qcfg
  130. if qcg > 0 and kcg > 0:
  131. correct_qcfg = 100 * qcg * kcg
  132. append_report_line(lines, f" 计算过程:100 × {qcg} × {kcg} = {correct_qcfg:.2f} m³/min")
  133. if abs(correct_qcfg - input_qcfg) < 0.1:
  134. append_report_line(lines, f" ✓ 验算通过:输入值 {input_qcfg} m³/min 与计算值 {correct_qcfg:.2f} m³/min")
  135. qcfg_valid = True
  136. else:
  137. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qcfg} m³/min 与计算值 {correct_qcfg:.2f} m³/min,建议改为 {correct_qcfg:.2f}")
  138. qcfg_valid = False
  139. else:
  140. append_report_line(lines, " ⚠ 无法验算:缺少 qcg 或 kcg 参数")
  141. # 2. 二氧化碳风量
  142. qcc = get_safe_value(face.get("qcc"), 0)
  143. kcc = get_safe_value(face.get("kcc"), 0)
  144. input_qcfc = get_safe_value(face.get("Qcfc"), 0)
  145. append_report_line(lines, "【验算二】按二氧化碳涌出量计算风量 (Qcfc)")
  146. append_report_line(lines, " 计算公式:Qcfc = 67 × qcc × kcc")
  147. append_report_line(lines, f" 参数取值:qcc = {qcc} m³/min, kcc = {kcc}")
  148. qcfc_valid = True
  149. correct_qcfc = input_qcfc
  150. if qcc > 0 and kcc > 0:
  151. correct_qcfc = 67 * qcc * kcc
  152. append_report_line(lines, f" 计算过程:67 × {qcc} × {kcc} = {correct_qcfc:.2f} m³/min")
  153. if abs(correct_qcfc - input_qcfc) < 0.1:
  154. append_report_line(lines, f" ✓ 验算通过:输入值 {input_qcfc} m³/min 与计算值 {correct_qcfc:.2f} m³/min")
  155. qcfc_valid = True
  156. else:
  157. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qcfc} m³/min 与计算值 {correct_qcfc:.2f} m³/min,建议改为 {correct_qcfc:.2f}")
  158. qcfc_valid = False
  159. else:
  160. append_report_line(lines, " ⚠ 无法验算:缺少 qcc 或 kcc 参数")
  161. # 3. 炸药量风量
  162. acf = face.get("Acf")
  163. input_qcfa = get_safe_value(face.get("Qcfa"), 0)
  164. append_report_line(lines, "【验算三】按炸药量计算风量 (Qcfa)")
  165. append_report_line(lines, " 计算公式:Qcfa = 25 × Acf")
  166. append_report_line(lines, f" 参数取值:Acf = {acf} kg")
  167. qcfa_valid = True
  168. correct_qcfa = input_qcfa if input_qcfa else 0
  169. if acf and acf > 0:
  170. correct_qcfa = 25 * acf
  171. append_report_line(lines, f" 计算过程:25 × {acf} = {correct_qcfa:.2f} m³/min")
  172. if abs(correct_qcfa - input_qcfa) < 0.1:
  173. append_report_line(lines, f" ✓ 验算通过:输入值 {input_qcfa} m³/min 与计算值 {correct_qcfa:.2f} m³/min")
  174. qcfa_valid = True
  175. else:
  176. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qcfa} m³/min 与计算值 {correct_qcfa:.2f} m³/min,建议改为 {correct_qcfa:.2f}")
  177. qcfa_valid = False
  178. else:
  179. append_report_line(lines, " ⚠ 无炸药量数据(非炮采工艺),跳过此项验算")
  180. # 4. 人员风量
  181. ncfi = get_safe_value(face.get("Ncfi"), 0)
  182. input_qcfn = get_safe_value(face.get("Qcfn"), 0)
  183. append_report_line(lines, "【验算四】按工作人员数量计算风量 (Qcfn)")
  184. append_report_line(lines, " 计算公式:Qcfn = 4 × Ncfi")
  185. append_report_line(lines, f" 参数取值:Ncfi = {ncfi} 人")
  186. qcfn_valid = True
  187. correct_qcfn = input_qcfn
  188. if ncfi > 0:
  189. correct_qcfn = 4 * ncfi
  190. append_report_line(lines, f" 计算过程:4 × {ncfi} = {correct_qcfn} m³/min")
  191. if correct_qcfn == input_qcfn:
  192. append_report_line(lines, f" ✓ 验算通过:输入值 {input_qcfn} m³/min 与计算值 {correct_qcfn} m³/min")
  193. qcfn_valid = True
  194. else:
  195. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qcfn} m³/min 与计算值 {correct_qcfn} m³/min,建议改为 {correct_qcfn}")
  196. qcfn_valid = False
  197. else:
  198. append_report_line(lines, " ⚠ 无法验算:缺少 Ncfi 参数")
  199. # 5. 温度参考风量
  200. append_report_line(lines, "【验算五】按温度条件计算风量(参考验证)")
  201. append_report_line(lines, " 计算公式:Qcfi = 60 × 70% × vcfi × Scfi × kchi × kcli")
  202. vcf = get_safe_value(face.get("vcf"), 0)
  203. scf = get_safe_value(face.get("scf"), 0)
  204. kch = get_safe_value(face.get("kch"), 1)
  205. kcl = get_safe_value(face.get("kcl"), 1)
  206. temperature = get_safe_value(face.get("average_temperature"), 20)
  207. param_notes = []
  208. if vcf <= 0 and temperature > 0:
  209. vcf = get_vcf_func(temperature)
  210. param_notes.append(f"vcf 未提供,根据温度 {temperature}℃ 自动取值 {vcf} m/s")
  211. if scf <= 0:
  212. max_control = get_safe_value(face.get("max_control_distance"), 0)
  213. min_control = get_safe_value(face.get("min_control_distance"), 0)
  214. height = get_safe_value(face.get("average_mining_height"), 0)
  215. if max_control > 0 and min_control > 0 and height > 0:
  216. scf = (max_control + min_control) / 2 * height
  217. param_notes.append(f"scf 自动计算:({max_control}+{min_control})/2×{height} = {scf:.2f} m²")
  218. if kch <= 0 and height > 0:
  219. if height <= 2.0:
  220. kch = 1.0
  221. elif height <= 2.5:
  222. kch = 1.1
  223. elif height <= 3.0:
  224. kch = 1.2
  225. elif height <= 4.0:
  226. kch = 1.3
  227. elif height <= 5.0:
  228. kch = 1.4
  229. else:
  230. kch = 1.5
  231. param_notes.append(f"kch 按采高自动取值 {kch}")
  232. for note in param_notes:
  233. append_report_line(lines, f" 注:{note}")
  234. temp_calculated = 0
  235. if vcf > 0 and scf > 0:
  236. temp_calculated = 60 * 0.7 * vcf * scf * kch * kcl
  237. append_report_line(lines, f" 计算过程:60 × 0.7 × {vcf} × {scf:.2f} × {kch} × {kcl} = {temp_calculated:.2f} m³/min")
  238. append_report_line(lines, " ℹ 此值为参考值,不作为取最大值依据")
  239. else:
  240. append_report_line(lines, " ⚠ 无法计算:缺少必要参数")
  241. # 6. 最大需风量
  242. input_q_max = get_safe_value(face.get("q_max"), 0)
  243. valid_values = []
  244. value_sources = []
  245. if correct_qcfg > 0:
  246. valid_values.append(correct_qcfg)
  247. value_sources.append(f"瓦斯计算({correct_qcfg:.2f})")
  248. if correct_qcfc > 0:
  249. valid_values.append(correct_qcfc)
  250. value_sources.append(f"CO2计算({correct_qcfc:.2f})")
  251. if correct_qcfa > 0:
  252. valid_values.append(correct_qcfa)
  253. value_sources.append(f"炸药计算({correct_qcfa:.2f})")
  254. if correct_qcfn > 0:
  255. valid_values.append(correct_qcfn)
  256. value_sources.append(f"人员计算({correct_qcfn:.2f})")
  257. if temp_calculated > 0:
  258. valid_values.append(temp_calculated)
  259. value_sources.append(f"温度计算({temp_calculated:.2f})")
  260. append_report_line(lines, "【验算六】最大需风量 (q_max)")
  261. append_report_line(lines, " 取值原则:取各项计算值的最大值")
  262. correct_qmax = input_q_max
  263. q_max_valid = True
  264. if valid_values:
  265. correct_qmax = max(valid_values)
  266. max_source = value_sources[valid_values.index(correct_qmax)]
  267. append_report_line(lines, f" 各项计算值:{', '.join(value_sources)}")
  268. append_report_line(lines, f" 最大值来源:{max_source}")
  269. append_report_line(lines, f" 正确 q_max = {correct_qmax:.2f} m³/min")
  270. if abs(correct_qmax - input_q_max) < 0.1:
  271. append_report_line(lines, f" ✓ 验算通过:输入值 {input_q_max} m³/min 与计算值 {correct_qmax:.2f} m³/min")
  272. q_max_valid = True
  273. else:
  274. append_report_line(lines, f" ✗ 验算不通过,建议修改为 {correct_qmax:.2f} m³/min")
  275. q_max_valid = False
  276. # 7. 风速验算
  277. append_report_line(lines, "【验算七】风速验算")
  278. append_report_line(lines, " 验算公式:15 × Scf ≤ Q_max ≤ 240 × Scf")
  279. computed_scf_area = get_safe_value(face.get("scf"), 0)
  280. if computed_scf_area <= 0:
  281. max_control = get_safe_value(face.get("max_control_distance"), 0)
  282. min_control = get_safe_value(face.get("min_control_distance"), 0)
  283. height = get_safe_value(face.get("average_mining_height"), 0)
  284. if max_control > 0 and min_control > 0 and height > 0:
  285. computed_scf_area = (max_control + min_control) / 2 * height
  286. append_report_line(lines, f" 断面积 Scf = ({max_control}+{min_control})/2×{height} = {computed_scf_area:.2f} m²")
  287. is_min_pass = True
  288. is_max_pass = True
  289. if computed_scf_area > 0:
  290. min_air = 15 * computed_scf_area
  291. max_air = 240 * computed_scf_area
  292. actual_speed = correct_qmax / (60 * computed_scf_area) if correct_qmax > 0 else 0
  293. append_report_line(lines, f" 参数:Scf = {computed_scf_area:.2f} m²")
  294. append_report_line(lines, f" 最低风量:{min_air:.2f} m³/min (0.25 m/s)")
  295. append_report_line(lines, f" 最高风量:{max_air:.2f} m³/min (4.0 m/s)")
  296. append_report_line(lines, f" 实际风量:{correct_qmax:.2f},实际风速:{actual_speed:.2f} m/s")
  297. is_min_pass = correct_qmax >= min_air
  298. is_max_pass = correct_qmax <= max_air
  299. if is_min_pass and is_max_pass:
  300. append_report_line(lines, " ✓ 风速验算通过")
  301. else:
  302. append_report_line(lines, " ✗ 风速验算不通过")
  303. # 8. 胶轮车
  304. append_report_line(lines, "【验算八】胶轮车风量验算")
  305. input_vehicle_pass = get_safe_bool(face.get("is_wheeled_vehicle_air_check_pass"))
  306. append_report_line(lines, f" 胶轮车验算结果:{input_vehicle_pass}")
  307. # 汇总
  308. all_valid = qcfg_valid and qcfc_valid and qcfa_valid and qcfn_valid and q_max_valid
  309. append_report_line(lines, f"{'─' * 90}")
  310. append_report_line(lines, f"【验算汇总】{face_name}")
  311. append_report_line(lines, f" {'✓ 全部验算通过' if all_valid else '✗ 存在验算不通过项'}")
  312. if not all_valid:
  313. append_report_line(lines, " 不通过项:")
  314. if not qcfg_valid:
  315. append_report_line(lines, f" - 瓦斯计算:输入 {input_qcfg},应为 {correct_qcfg:.2f}")
  316. if not qcfc_valid:
  317. append_report_line(lines, f" - CO2计算:输入 {input_qcfc},应为 {correct_qcfc:.2f}")
  318. if not qcfa_valid:
  319. append_report_line(lines, f" - 炸药计算:输入 {input_qcfa},应为 {correct_qcfa:.2f}")
  320. if not qcfn_valid:
  321. append_report_line(lines, f" - 人员计算:输入 {input_qcfn},应为 {correct_qcfn}")
  322. if not q_max_valid:
  323. append_report_line(lines, f" - 最大风量:输入 {input_q_max},应为 {correct_qmax:.2f}")
  324. result = {
  325. "face_name": face_name,
  326. "is_standby": False,
  327. "all_valid": all_valid,
  328. "q_max_correct": correct_qmax,
  329. "wind_check_pass": is_min_pass and is_max_pass
  330. }
  331. return result, "".join(lines)
  332. def _verify_standby_working_face(face, normal_results):
  333. """备用工作面验算(内部私有函数)"""
  334. face_name = get_safe_value(face.get("face_name"), "Unknown")
  335. standby_base = get_safe_value(face.get("standby_base"), "")
  336. planned_qmax = get_safe_value(face.get("q_max"), 0)
  337. lines = []
  338. append_report_line(lines, f"{'─' * 90}")
  339. append_report_line(lines, f"【工作面:{face_name}】")
  340. append_report_line(lines, " 类型:备用工作面")
  341. append_report_line(lines, f"{'─' * 90}")
  342. append_report_line(lines, "【验算】备用工作面需风量")
  343. append_report_line(lines, " 计算公式:Q_备用 = 0.5 × Q_正常")
  344. append_report_line(lines, f" 依据工作面:{standby_base}")
  345. all_valid = False
  346. correct_qmax = planned_qmax
  347. if standby_base and standby_base in normal_results:
  348. base_q_max = normal_results[standby_base]["q_max_correct"]
  349. correct_qmax = base_q_max * 0.5
  350. append_report_line(lines, f" 计算:0.5 × {base_q_max:.2f} = {correct_qmax:.2f} m³/min")
  351. if abs(correct_qmax - planned_qmax) < 0.1:
  352. append_report_line(lines, f" ✓ 验算通过:输入 {planned_qmax} 与计算值 {correct_qmax:.2f} 一致")
  353. all_valid = True
  354. else:
  355. append_report_line(lines, f" ✗ 验算不通过,建议改为 {correct_qmax:.2f}")
  356. all_valid = False
  357. else:
  358. append_report_line(lines, " ✗ 验算失败:关联工作面不存在")
  359. append_report_line(lines, f"{'─' * 90}")
  360. append_report_line(lines, f"【验算汇总】{face_name}")
  361. append_report_line(lines, f" {'✓ 验算通过' if all_valid else '✗ 验算不通过'}")
  362. result = {
  363. "face_name": face_name,
  364. "is_standby": True,
  365. "standby_base": standby_base,
  366. "all_valid": all_valid,
  367. "q_max_correct": correct_qmax
  368. }
  369. return result, "".join(lines)
  370. # ===================== 2. 掘进工作面验算工具 =====================
  371. @tool
  372. def verify_tunneling_face_ventilation(
  373. state: Annotated[dict, InjectedState]
  374. ) -> str:
  375. """验算掘进工作面需风量计算是否正确"""
  376. vent_plan = state.get("vent_plan_data", {})
  377. tunneling_faces = vent_plan.get("tunneling_faces", [])
  378. if not tunneling_faces:
  379. return "❌ 未获取到掘进工作面数据"
  380. report_lines = []
  381. append_report_line(report_lines, "验算掘进工作面需风量计算是否正确")
  382. results = []
  383. for face in tunneling_faces:
  384. res, report = _verify_single_tunneling_face(face)
  385. append_report_line(report_lines, report)
  386. results.append(res)
  387. append_report_line(report_lines, format_tunneling_summary(results))
  388. return "".join(report_lines)
  389. def _verify_single_tunneling_face(face):
  390. """单条掘进工作面验算(内部)"""
  391. face_name = get_safe_value(face.get("face_name"), "Unknown")
  392. lines = []
  393. append_report_line(lines, f"【掘进工作面:{face_name}】")
  394. # 瓦斯风量
  395. qhg = get_safe_value(face.get("qhg"), 0)
  396. khg = get_safe_value(face.get("khg"), 0)
  397. input_qhfg = get_safe_value(face.get("Qhfg"), 0)
  398. is_gas_rock = get_safe_bool(face.get("is_gas_emission_rock_tunnel"))
  399. append_report_line(lines, "【验算一】按瓦斯涌出量计算风量 (Qhfg)")
  400. append_report_line(lines, " 公式:Qhf = 100 × qhg × khg")
  401. append_report_line(lines, f" qhg={qhg}, khg={khg}")
  402. qhfg_valid = True
  403. qhfg_correct = input_qhfg
  404. if qhg > 0 and khg > 0:
  405. correct_qhfg = 100 * qhg * khg
  406. append_report_line(lines, f" 计算:100×{qhg}×{khg}={correct_qhfg:.2f}")
  407. if abs(correct_qhfg - input_qhfg) < 0.1:
  408. qhfg_valid = True
  409. else:
  410. append_report_line(lines, f" ✗ 不一致,建议 {correct_qhfg:.2f}")
  411. qhfg_valid = False
  412. qhfg_correct = correct_qhfg
  413. # CO2风量
  414. qhc = get_safe_value(face.get("qhc"), 0)
  415. khc = get_safe_value(face.get("khc"), 0)
  416. input_qhfc = get_safe_value(face.get("Qhfc"), 0)
  417. append_report_line(lines, "【验算二】按二氧化碳涌出量计算风量 (Qhfc)")
  418. append_report_line(lines, " 公式:Qhf = 67 × qhc × khc")
  419. append_report_line(lines, f" qhc={qhc}, khc={khc}")
  420. qhfc_valid = True
  421. qhfc_correct = input_qhfc
  422. if qhc > 0 and khc > 0:
  423. correct_qhfc = 67 * qhc * khc
  424. append_report_line(lines, f" 计算:67×{qhc}×{khc}={correct_qhfc:.2f}")
  425. if abs(correct_qhfc - input_qhfc) < 0.1:
  426. qhfc_valid = True
  427. else:
  428. append_report_line(lines, f" ✗ 不一致,建议 {correct_qhfc:.2f}")
  429. qhfc_valid = False
  430. qhfc_correct = correct_qhfc
  431. # 局部通风机风量
  432. eQaf = get_safe_value(face.get("eQaf"), 0)
  433. input_qhfl = get_safe_value(face.get("Qhfl"), 0)
  434. has_leak = get_safe_bool(face.get("has_min_air_by_leak_rate"))
  435. is_gas_tunnel = get_safe_bool(face.get("is_gas_emission_tunnel"))
  436. shdi = get_safe_value(face.get("Shdi"), 0)
  437. append_report_line(lines, "【验算三】按局部通风机吸风量 (Qhfl)")
  438. wind_coeff = 0.25 if is_gas_tunnel else 0.15
  439. correct_qhfl = eQaf + 60 * wind_coeff * shdi
  440. append_report_line(lines, f" 计算:{eQaf} + 60×{wind_coeff}×{shdi} = {correct_qhfl:.2f}")
  441. qhfl_valid = abs(correct_qhfl - input_qhfl) < 0.1
  442. if not qhfl_valid:
  443. append_report_line(lines, f" ✗ 不一致,建议 {correct_qhfl:.2f}")
  444. # 省略其余原有业务逻辑...
  445. return {"face_name": face_name, "all_valid": qhfg_valid and qhfc_valid and qhfl_valid}, "".join(lines)
  446. # ===================== 3. 硐室、其他用风地点工具 =====================
  447. @tool
  448. def verify_chamber_ventilation(
  449. state: Annotated[dict, InjectedState]
  450. ) -> str:
  451. """验算硐室需风量"""
  452. vent_plan = state.get("vent_plan_data", {})
  453. chambers = vent_plan.get("chambers", [])
  454. if not chambers:
  455. return "❌ 未获取到硐室数据"
  456. report_lines = []
  457. append_report_line(report_lines, "硐室需风量验算报告")
  458. results = []
  459. for chamber in chambers:
  460. res, rep = _verify_single_chamber(chamber)
  461. append_report_line(report_lines, rep)
  462. results.append(res)
  463. append_report_line(report_lines, format_chamber_summary(results))
  464. return "".join(report_lines)
  465. @tool
  466. def verify_other_points_ventilation(
  467. state: Annotated[dict, InjectedState]
  468. ) -> str:
  469. """验算其他用风地点需风量"""
  470. vent_plan = state.get("vent_plan_data", {})
  471. other_points = vent_plan.get("other_points", [])
  472. if not other_points:
  473. return "❌ 未获取到其他用风地点数据"
  474. report_lines = []
  475. append_report_line(report_lines, "其他用风地点需风量验算报告")
  476. results = []
  477. for point in other_points:
  478. res, rep = _verify_single_other_point(point)
  479. append_report_line(report_lines, rep)
  480. results.append(res)
  481. append_report_line(report_lines, format_other_points_summary(results))
  482. return "".join(report_lines)
  483. def _verify_single_other_point(point):
  484. """验算单个其他用风地点需风量,返回(结果字典, 报告字符串)"""
  485. name = get_safe_value(point.get('name'), 'Unknown')
  486. input_qo = get_safe_value(point.get('Qo'), 0)
  487. has_calc_check = get_safe_bool(point.get('has_calc_check_process'), False)
  488. lines = []
  489. append_report_line(lines, f"【用风地点:{name}】")
  490. # 存储各项计算结果
  491. valid_values = []
  492. value_sources = []
  493. # ==================== 1. 按瓦斯涌出量计算风量 ====================
  494. qrg = get_safe_value(point.get('qrg'), 0)
  495. krg = get_safe_value(point.get('krg'), 0)
  496. input_qrlg = get_safe_value(point.get('Qrlg'), 0)
  497. append_report_line(lines, f"【验算一】按瓦斯涌出量计算风量 (Qrlg)")
  498. append_report_line(lines, f" 计算公式:Qrlg = 133 × qrg × krg")
  499. append_report_line(lines, f" 说明:其他用风巷道允许瓦斯浓度不超过0.75%,故系数取133")
  500. append_report_line(lines, f" 参数取值:qrg = {qrg} m³/min, krg = {krg}")
  501. if qrg > 0 and krg > 0:
  502. correct_qrlg = 133 * qrg * krg
  503. append_report_line(lines, f" 计算过程:133 × {qrg} × {krg} = {correct_qrlg:.2f} m³/min")
  504. if input_qrlg == 0:
  505. append_report_line(lines, f" ⚠ 输入值为空,建议填写为:{correct_qrlg:.2f} m³/min")
  506. qrlg_valid = False
  507. qrlg_correct = correct_qrlg
  508. elif abs(correct_qrlg - input_qrlg) < 0.1:
  509. append_report_line(lines, f" ✓ 验算通过:输入值 {input_qrlg} m³/min 与计算值 {correct_qrlg:.2f} m³/min 一致")
  510. qrlg_valid = True
  511. qrlg_correct = correct_qrlg
  512. else:
  513. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qrlg} m³/min 与计算值 {correct_qrlg:.2f} m³/min 不符")
  514. append_report_line(lines, f" 建议修改为:{correct_qrlg:.2f} m³/min")
  515. qrlg_valid = False
  516. qrlg_correct = correct_qrlg
  517. if correct_qrlg > 0:
  518. valid_values.append(correct_qrlg)
  519. value_sources.append(f"瓦斯计算({correct_qrlg:.2f})")
  520. else:
  521. append_report_line(lines, f" ⚠ 无法验算:缺少 qrg 或 krg 参数")
  522. qrlg_valid = True
  523. qrlg_correct = input_qrlg
  524. if input_qrlg > 0:
  525. valid_values.append(input_qrlg)
  526. value_sources.append(f"瓦斯计算({input_qrlg:.2f})")
  527. # ==================== 2. 按风速验算(一般巷道) ====================
  528. src = get_safe_value(point.get('Src'), 0)
  529. append_report_line(lines, f"【验算二】按风速验算(一般巷道)")
  530. append_report_line(lines, f" 计算公式:Qrl ≥ 60 × 0.15 × Src = 9 × Src(最低风速0.15 m/s)")
  531. append_report_line(lines, f" 参数取值:Src = {src} m²")
  532. if src > 0:
  533. correct_q_by_wind_normal = 60 * 0.15 * src # 9 × src
  534. append_report_line(lines, f" 计算过程:60 × 0.15 × {src} = {correct_q_by_wind_normal:.2f} m³/min")
  535. append_report_line(lines, f" ℹ 一般巷道最低需风量按最低风速0.15 m/s计算")
  536. if correct_q_by_wind_normal > 0:
  537. valid_values.append(correct_q_by_wind_normal)
  538. value_sources.append(f"一般巷道风速计算({correct_q_by_wind_normal:.2f})")
  539. else:
  540. append_report_line(lines, f" ⚠ 无法计算:缺少一般巷道净断面积 Src 参数")
  541. correct_q_by_wind_normal = 0
  542. # ==================== 3. 按风速验算(架线电机车巷道) ====================
  543. sre = get_safe_value(point.get('Sre'), 0)
  544. has_gas_emission = get_safe_bool(point.get('has_gas_emission'), False)
  545. append_report_line(lines, f"【验算三】按风速验算(架线电机车巷道)")
  546. if sre > 0:
  547. if has_gas_emission:
  548. # 有瓦斯涌出:最低风速 1.0 m/s
  549. correct_q_by_tram = 60 * 1.0 * sre # 60 × sre
  550. append_report_line(lines, f" 巷道类型:有瓦斯涌出的架线电机车巷道")
  551. append_report_line(lines, f" 计算公式:Qrl ≥ 60 × 1.0 × Sre = 60 × Sre(最低风速1.0 m/s)")
  552. append_report_line(lines, f" 计算过程:60 × 1.0 × {sre} = {correct_q_by_tram:.2f} m³/min")
  553. else:
  554. # 无瓦斯涌出:最低风速 0.5 m/s
  555. correct_q_by_tram = 60 * 0.5 * sre # 30 × sre
  556. append_report_line(lines, f" 巷道类型:无瓦斯涌出的架线电机车巷道")
  557. append_report_line(lines, f" 计算公式:Qrl ≥ 60 × 0.5 × Sre = 30 × Sre(最低风速0.5 m/s)")
  558. append_report_line(lines, f" 计算过程:60 × 0.5 × {sre} = {correct_q_by_tram:.2f} m³/min")
  559. append_report_line(lines, f" 参数取值:Sre = {sre} m²")
  560. if correct_q_by_tram > 0:
  561. valid_values.append(correct_q_by_tram)
  562. value_sources.append(f"架线电机车风速计算({correct_q_by_tram:.2f})")
  563. else:
  564. append_report_line(lines, f" ⚠ 无法计算:缺少架线电机车巷道净断面积 Sre 参数")
  565. correct_q_by_tram = 0
  566. # ==================== 4. 按防爆柴油动力装置机车需要风量验算 ====================
  567. ndl = get_safe_value(point.get('Ndl'), 0)
  568. pdl = get_safe_value(point.get('Pdl'), 0)
  569. input_qrlv = get_safe_value(point.get('Qrlv'), 0)
  570. append_report_line(lines, f"【验算四】按防爆柴油动力装置机车需要风量验算 (Qrlv)")
  571. append_report_line(lines, f" 计算公式:Qrlv = 4.0 × Ndl × Pdl")
  572. append_report_line(lines, f" 参数取值:Ndl = {ndl} 台, Pdl = {pdl} kW")
  573. if ndl > 0 and pdl > 0:
  574. correct_qrlv = 4.0 * ndl * pdl
  575. append_report_line(lines, f" 计算过程:4.0 × {ndl} × {pdl} = {correct_qrlv:.2f} m³/min")
  576. if input_qrlv == 0:
  577. append_report_line(lines, f" ⚠ 输入值为空,建议填写为:{correct_qrlv:.2f} m³/min")
  578. qrlv_valid = False
  579. qrlv_correct = correct_qrlv
  580. elif abs(correct_qrlv - input_qrlv) < 0.1:
  581. append_report_line(lines, f" ✓ 验算通过:输入值 {input_qrlv} m³/min 与计算值 {correct_qrlv:.2f} m³/min 一致")
  582. qrlv_valid = True
  583. qrlv_correct = correct_qrlv
  584. else:
  585. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qrlv} m³/min 与计算值 {correct_qrlv:.2f} m³/min 不符")
  586. append_report_line(lines, f" 建议修改为:{correct_qrlv:.2f} m³/min")
  587. qrlv_valid = False
  588. qrlv_correct = correct_qrlv
  589. if correct_qrlv > 0:
  590. valid_values.append(correct_qrlv)
  591. value_sources.append(f"柴油装置计算({correct_qrlv:.2f})")
  592. else:
  593. if ndl > 0 and pdl == 0:
  594. append_report_line(lines, f" ⚠ 无法验算:缺少防爆柴油动力装置机车功率 Pdl 参数")
  595. elif pdl > 0 and ndl == 0:
  596. append_report_line(lines, f" ⚠ 无法验算:缺少防爆柴油动力装置机车台数 Ndl 参数")
  597. else:
  598. append_report_line(lines, f" ℹ 无防爆柴油动力装置机车,跳过此项验算")
  599. qrlv_valid = True
  600. qrlv_correct = input_qrlv
  601. if input_qrlv > 0:
  602. valid_values.append(input_qrlv)
  603. value_sources.append(f"柴油装置计算({input_qrlv:.2f})")
  604. # ==================== 5. 验算最大需风量 Qo ====================
  605. append_report_line(lines, f"【验算五】最大需风量 (Qo)")
  606. append_report_line(lines, f" 取值原则:取各项计算值的最大值")
  607. if valid_values:
  608. correct_qo = max(valid_values)
  609. max_source = value_sources[valid_values.index(correct_qo)]
  610. append_report_line(lines, f" 各项计算值:{', '.join(value_sources)}")
  611. append_report_line(lines, f" 最大值来源:{max_source}")
  612. append_report_line(lines, f" 正确 Qo = {correct_qo:.2f} m³/min")
  613. if input_qo == 0:
  614. append_report_line(lines, f" ⚠ 输入值为空,建议填写为:{correct_qo:.2f} m³/min")
  615. qo_valid = False
  616. elif abs(correct_qo - input_qo) < 0.1:
  617. append_report_line(lines, f" ✓ 验算通过:输入值 {input_qo} m³/min 与计算值 {correct_qo:.2f} m³/min 一致")
  618. qo_valid = True
  619. else:
  620. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qo} m³/min 与计算值 {correct_qo:.2f} m³/min 不符")
  621. append_report_line(lines, f" 建议修改为:{correct_qo:.2f} m³/min")
  622. qo_valid = False
  623. else:
  624. append_report_line(lines, f" ⚠ 无法验算:没有有效的计算值,请检查输入参数")
  625. correct_qo = input_qo
  626. qo_valid = False
  627. # ==================== 6. 风速验算(基于实际需风量,验证最高风速) ====================
  628. append_report_line(lines, f"【验算六】最高风速验算(基于正确需风量)")
  629. append_report_line(lines, f" 验算公式:Qo ≤ 60 × 4.0 × S = 240 × S(最高风速4.0 m/s)")
  630. # 确定可用的断面积
  631. available_area = 0
  632. area_type = ""
  633. if src > 0:
  634. available_area = src
  635. area_type = "一般巷道"
  636. elif sre > 0:
  637. available_area = sre
  638. area_type = "架线电机车巷道"
  639. if available_area > 0 and correct_qo > 0:
  640. actual_speed = correct_qo / (60 * available_area)
  641. max_air = 240 * available_area
  642. append_report_line(lines, f" 巷道类型:{area_type}")
  643. append_report_line(lines, f" 断面积:{available_area:.2f} m²")
  644. append_report_line(lines, f" 最高允许风量:240 × {available_area:.2f} = {max_air:.2f} m³/min")
  645. append_report_line(lines, f" 实际需风量:{correct_qo:.2f} m³/min")
  646. append_report_line(lines, f" 实际风速:{correct_qo:.2f} ÷ (60 × {available_area:.2f}) = {actual_speed:.2f} m/s")
  647. is_max_pass = correct_qo <= max_air
  648. if is_max_pass:
  649. append_report_line(lines, f" ✓ 最高风速验算通过:实际风速 {actual_speed:.2f} m/s ≤ 4.0 m/s")
  650. else:
  651. append_report_line(lines, f" ✗ 最高风速验算不通过:实际风速 {actual_speed:.2f} m/s > 4.0 m/s")
  652. append_report_line(lines, f" 建议:降低供风量至不超过 {max_air:.2f} m³/min")
  653. else:
  654. append_report_line(lines, f" ⚠ 无法进行最高风速验算:缺少巷道净断面积参数或需风量")
  655. is_max_pass = True
  656. # ==================== 7. 核验过程标识 ====================
  657. append_report_line(lines, f"【验算七】风量计算核验过程")
  658. append_report_line(lines, f" 核验标识:has_calc_check_process = {has_calc_check}")
  659. if has_calc_check:
  660. append_report_line(lines, f" ✓ 已进行风量计算核验")
  661. else:
  662. append_report_line(lines, f" ⚠ 未进行风量计算核验,建议补充")
  663. # ==================== 汇总 ====================
  664. all_valid = qrlg_valid and qrlv_valid and qo_valid
  665. append_report_line(lines, f"{'─' * 90}")
  666. append_report_line(lines, f"【验算汇总】{name}")
  667. append_report_line(lines, f" {'✓ 全部验算通过' if all_valid else '✗ 存在验算不通过项'}")
  668. if not all_valid:
  669. append_report_line(lines, f" 不通过项:")
  670. if not qrlg_valid:
  671. if input_qrlg == 0:
  672. append_report_line(lines, f" - 瓦斯涌出量计算:输入值为空,应为 {qrlg_correct:.2f}")
  673. else:
  674. append_report_line(lines, f" - 瓦斯涌出量计算:输入 {input_qrlg},应为 {qrlg_correct:.2f}")
  675. if not qrlv_valid:
  676. if input_qrlv == 0:
  677. append_report_line(lines, f" - 柴油装置计算:输入值为空,应为 {qrlv_correct:.2f}")
  678. else:
  679. append_report_line(lines, f" - 柴油装置计算:输入 {input_qrlv},应为 {qrlv_correct:.2f}")
  680. if not qo_valid:
  681. if input_qo == 0:
  682. append_report_line(lines, f" - 最大需风量:输入值为空,应为 {correct_qo:.2f}")
  683. else:
  684. append_report_line(lines, f" - 最大需风量:输入 {input_qo},应为 {correct_qo:.2f}")
  685. # 添加建议
  686. append_report_line(lines, f" 建议措施:")
  687. suggestions_count = 0
  688. if not all_valid:
  689. append_report_line(lines, f" - 请根据上述不通过项修正输入参数")
  690. suggestions_count += 1
  691. if src == 0 and sre == 0:
  692. append_report_line(lines, f" - 请补充巷道净断面积参数(Src 或 Sre)")
  693. suggestions_count += 1
  694. if not is_max_pass:
  695. append_report_line(lines, f" - 最高风速验算不通过,请调整供风量")
  696. suggestions_count += 1
  697. if not has_calc_check:
  698. append_report_line(lines, f" - 建议补充风量计算核验过程")
  699. suggestions_count += 1
  700. if suggestions_count == 0:
  701. append_report_line(lines, f" - 用风地点风量计算正确,符合要求")
  702. result = {
  703. 'name': name,
  704. 'all_valid': all_valid,
  705. 'qo_correct': correct_qo,
  706. 'wind_check_pass': is_max_pass,
  707. 'has_calc_check': has_calc_check
  708. }
  709. return result, "".join(lines)
  710. def _verify_single_chamber(chamber):
  711. """验算单个硐室需风量,返回(结果字典, 报告字符串)"""
  712. name = get_safe_value(chamber.get('name'), 'Unknown')
  713. chamber_type = get_safe_value(chamber.get('type'), '其他')
  714. input_qd = get_safe_value(chamber.get('qd'), 0)
  715. has_calc_check = get_safe_bool(chamber.get('has_calc_check_process'), False)
  716. lines = []
  717. append_report_line(lines, f"【硐室:{name}】")
  718. append_report_line(lines, f" 类型:{chamber_type}")
  719. correct_qd = 0
  720. calculation_method = ""
  721. calculation_detail = ""
  722. can_calculate = False
  723. # ==================== 根据不同类型计算需风量 ====================
  724. if chamber_type == "爆炸物品库":
  725. append_report_line(lines, f"【验算】按爆炸物品库计算需风量")
  726. append_report_line(lines, f" 计算公式:Qd = 4 × V / 60(或按经验值计算)")
  727. V = get_safe_value(chamber.get('V'), 0)
  728. if V > 0:
  729. # 爆炸物品库按体积计算,每小时4次换气
  730. correct_qd = 4 * V / 60
  731. calculation_method = "爆炸物品库(按体积换气)"
  732. calculation_detail = f"4 × {V} ÷ 60 = {correct_qd:.2f}"
  733. append_report_line(lines, f" 参数取值:V = {V} m³")
  734. append_report_line(lines, f" 计算过程:4 × {V} ÷ 60 = {correct_qd:.2f} m³/min")
  735. append_report_line(lines, f" ℹ 爆炸物品库需保证每小时4次换气")
  736. can_calculate = True
  737. else:
  738. append_report_line(lines, f" ⚠ 无法计算:缺少硐室体积 V 参数")
  739. correct_qd = input_qd
  740. # 补充说明:爆炸物品库最低风量要求
  741. append_report_line(lines, f" 注:爆炸物品库最低风量不得小于 100 m³/min")
  742. if correct_qd > 0 and correct_qd < 100:
  743. append_report_line(lines, f" ⚠ 计算值 {correct_qd:.2f} m³/min 低于最低要求 100 m³/min")
  744. append_report_line(lines, f" 建议取值为:100 m³/min")
  745. correct_qd = 100
  746. elif chamber_type == "充电硐室":
  747. append_report_line(lines, f"【验算】按充电硐室计算需风量")
  748. append_report_line(lines, f" 计算公式:Qd = 60 × qhy × k")
  749. append_report_line(lines, f" 其中:qhy 为充电时氢气产生量,k 为氢气浓度系数")
  750. qhy = get_safe_value(chamber.get('qhy'), 0)
  751. if qhy > 0:
  752. # 充电硐室按氢气产生量计算,系数取200(氢气浓度控制在0.5%以下)
  753. k = 200
  754. correct_qd = 60 * qhy * k
  755. calculation_method = "充电硐室(按氢气产生量)"
  756. calculation_detail = f"60 × {qhy} × {k} = {correct_qd:.2f}"
  757. append_report_line(lines, f" 参数取值:qhy = {qhy} m³/min, 氢气浓度系数 k = {k}")
  758. append_report_line(lines, f" 计算过程:60 × {qhy} × {k} = {correct_qd:.2f} m³/min")
  759. append_report_line(lines, f" ℹ 按氢气产生量计算,确保氢气浓度不超过0.5%")
  760. can_calculate = True
  761. else:
  762. append_report_line(lines, f" ⚠ 无法计算:缺少氢气产生量 qhy 参数")
  763. correct_qd = input_qd
  764. elif chamber_type == "机电硐室":
  765. append_report_line(lines, f"【验算】按机电硐室计算需风量")
  766. # 修正后的公式:Qd (m³/min) = (3600 × s × W) / (ρ × CP × dt × 60)
  767. append_report_line(lines, f" 计算公式:Qd = (3600 × s × W) / (ρ × CP × dt × 60)")
  768. append_report_line(lines, f" 简化公式:Qd = (60 × s × W) / (ρ × CP × dt)")
  769. W = get_safe_value(chamber.get('W'), 0)
  770. s = get_safe_value(chamber.get('s'), 0)
  771. dt = get_safe_value(chamber.get('dt'), 0)
  772. ρ = get_safe_value(chamber.get('ρ'), 1.2) # 默认空气密度 1.2 kg/m³
  773. CP = get_safe_value(chamber.get('CP'), 1.01) # 默认空气比热 1.01 kJ/(kg·℃)
  774. append_report_line(lines, f" 参数取值:")
  775. append_report_line(lines, f" W = {W} kW(电动机/变压器总功率)")
  776. append_report_line(lines, f" s = {s}(发热系数)")
  777. append_report_line(lines, f" dt = {dt} ℃(进回风温差)")
  778. append_report_line(lines, f" ρ = {ρ} kg/m³(空气密度,默认1.2)")
  779. append_report_line(lines, f" CP = {CP} kJ/(kg·℃)(空气定压比热,默认1.01)")
  780. if W > 0 and s > 0 and dt > 0:
  781. # 使用修正后的公式(计算结果为 m³/min)
  782. correct_qd = (3600 * s * W) / (ρ * CP * dt * 60)
  783. calculation_method = "机电硐室(按热源散热)"
  784. calculation_detail = f"(3600 × {s} × {W}) ÷ ({ρ} × {CP} × {dt} × 60) = {correct_qd:.2f}"
  785. append_report_line(lines, f" 计算过程(完整公式):")
  786. append_report_line(lines, f" 分子:3600 × {s} × {W} = {3600 * s * W:.2f} kJ")
  787. append_report_line(lines, f" 分母:{ρ} × {CP} × {dt} × 60 = {ρ * CP * dt * 60:.2f} kJ/m³")
  788. append_report_line(lines, f" 结果:{correct_qd:.2f} m³/min")
  789. append_report_line(lines, f" 简化计算:60 × {s} × {W} ÷ ({ρ} × {CP} × {dt}) = {correct_qd:.2f} m³/min")
  790. can_calculate = True
  791. else:
  792. missing_params = []
  793. if W == 0:
  794. missing_params.append("功率 W")
  795. if s == 0:
  796. missing_params.append("发热系数 s")
  797. if dt == 0:
  798. missing_params.append("温差 dt")
  799. append_report_line(lines, f" ⚠ 无法计算:缺少必要参数 ({', '.join(missing_params)})")
  800. correct_qd = input_qd
  801. # 机电硐室最低风量要求
  802. if can_calculate and correct_qd > 0:
  803. append_report_line(lines, f" 注:机电硐室最低风量不得小于 150 m³/min")
  804. if correct_qd < 150:
  805. append_report_line(lines, f" ⚠ 计算值 {correct_qd:.2f} m³/min 低于最低要求 150 m³/min")
  806. append_report_line(lines, f" 建议取值为:150 m³/min")
  807. correct_qd = 150
  808. else: # 其他类型硐室
  809. append_report_line(lines, f"【验算】按其他类型硐室计算需风量")
  810. append_report_line(lines, f" 计算公式:Qd = 4 × V / 60(按体积计算)")
  811. V = get_safe_value(chamber.get('V'), 0)
  812. if V > 0:
  813. correct_qd = 4 * V / 60
  814. calculation_method = "其他硐室(按体积换气)"
  815. calculation_detail = f"4 × {V} ÷ 60 = {correct_qd:.2f}"
  816. append_report_line(lines, f" 参数取值:V = {V} m³")
  817. append_report_line(lines, f" 计算过程:4 × {V} ÷ 60 = {correct_qd:.2f} m³/min")
  818. append_report_line(lines, f" ℹ 按每小时4次换气计算")
  819. can_calculate = True
  820. else:
  821. append_report_line(lines, f" ⚠ 无法计算:缺少硐室体积 V 参数")
  822. correct_qd = input_qd
  823. # 其他硐室最低风量要求
  824. if can_calculate and correct_qd > 0:
  825. append_report_line(lines, f" 注:硐室最低风量不得小于 100 m³/min")
  826. if correct_qd < 100:
  827. append_report_line(lines, f" ⚠ 计算值 {correct_qd:.2f} m³/min 低于最低要求 100 m³/min")
  828. append_report_line(lines, f" 建议取值为:100 m³/min")
  829. correct_qd = 100
  830. # ==================== 验算实际需风量 ====================
  831. append_report_line(lines, f"【验算】实际需风量验证")
  832. append_report_line(lines, f" 计算值:{correct_qd:.2f} m³/min({calculation_method})")
  833. if input_qd == 0:
  834. append_report_line(lines, f" 输入值:未填写(或为0)")
  835. append_report_line(lines, f" ⚠ 输入值为空,建议填写为:{correct_qd:.2f} m³/min")
  836. qd_valid = False
  837. elif abs(correct_qd - input_qd) < 0.1:
  838. append_report_line(lines, f" 输入值:{input_qd} m³/min")
  839. append_report_line(lines, f" ✓ 验算通过:输入值与计算值一致")
  840. qd_valid = True
  841. else:
  842. append_report_line(lines, f" 输入值:{input_qd} m³/min")
  843. append_report_line(lines, f" ✗ 验算不通过:输入值 {input_qd} m³/min 与计算值 {correct_qd:.2f} m³/min 不符")
  844. append_report_line(lines, f" 建议修改为:{correct_qd:.2f} m³/min")
  845. qd_valid = False
  846. # ==================== 风速验算(硐室通用) ====================
  847. append_report_line(lines, f"【验算】风速验算")
  848. append_report_line(lines, f" 验算公式:9 × S ≤ Qd ≤ 240 × S(硐室风速 0.15 ~ 4.0 m/s)")
  849. # 对于硐室,如果没有提供断面积,根据体积和高度估算
  850. V = get_safe_value(chamber.get('V'), 0)
  851. if V > 0 and can_calculate:
  852. # 估算硐室高度(通常2.5-3.5m),取平均3.0m
  853. estimated_height = 3.0
  854. estimated_area = V / estimated_height
  855. append_report_line(lines, f" 参数估算:硐室体积 V = {V} m³,估算高度 {estimated_height} m")
  856. append_report_line(lines, f" 估算断面积 S ≈ V ÷ 高度 = {V} ÷ {estimated_height} = {estimated_area:.2f} m²")
  857. min_air = 9 * estimated_area # 最低风速0.15 m/s × 60 = 9
  858. max_air = 240 * estimated_area # 最高风速4.0 m/s × 60 = 240
  859. actual_speed = correct_qd / (60 * estimated_area) if estimated_area > 0 else 0
  860. append_report_line(lines, f" 最低需风量:9 × {estimated_area:.2f} = {min_air:.2f} m³/min(对应风速 0.15 m/s)")
  861. append_report_line(lines, f" 最高需风量:240 × {estimated_area:.2f} = {max_air:.2f} m³/min(对应风速 4.0 m/s)")
  862. append_report_line(lines, f" 实际需风量:{correct_qd:.2f} m³/min")
  863. append_report_line(lines, f" 实际风速:{correct_qd:.2f} ÷ (60 × {estimated_area:.2f}) = {actual_speed:.2f} m/s")
  864. is_min_pass = correct_qd >= min_air
  865. is_max_pass = correct_qd <= max_air
  866. if is_min_pass and is_max_pass:
  867. append_report_line(lines, f" ✓ 风速验算通过:实际风速 {actual_speed:.2f} m/s 在允许范围 [0.15, 4.0] m/s 内")
  868. elif not is_min_pass:
  869. append_report_line(lines, f" ✗ 风速验算不通过:实际风速 {actual_speed:.2f} m/s 低于最低风速 0.15 m/s")
  870. append_report_line(lines, f" 建议:增加供风量至不低于 {min_air:.2f} m³/min")
  871. else:
  872. append_report_line(lines, f" ✗ 风速验算不通过:实际风速 {actual_speed:.2f} m/s 超过最大风速 4.0 m/s")
  873. append_report_line(lines, f" 建议:降低供风量至不超过 {max_air:.2f} m³/min")
  874. else:
  875. append_report_line(lines, f" ⚠ 无法进行风速验算:缺少硐室体积 V 参数")
  876. is_min_pass = True
  877. is_max_pass = True
  878. # ==================== 核验过程标识 ====================
  879. append_report_line(lines, f"【验算】风量计算核验过程")
  880. append_report_line(lines, f" 核验标识:has_calc_check_process = {has_calc_check}")
  881. if has_calc_check:
  882. append_report_line(lines, f" ✓ 已进行风量计算核验")
  883. else:
  884. append_report_line(lines, f" ⚠ 未进行风量计算核验,建议补充")
  885. # ==================== 汇总 ====================
  886. all_valid = qd_valid
  887. append_report_line(lines, f"{'─' * 90}")
  888. append_report_line(lines, f"【验算汇总】{name}")
  889. append_report_line(lines, f" {'✓ 验算通过' if all_valid else '✗ 验算不通过'}")
  890. if not all_valid:
  891. append_report_line(lines, f" 不通过项:")
  892. if not qd_valid:
  893. if input_qd == 0:
  894. append_report_line(lines, f" - 实际需风量:输入值为空,应为 {correct_qd:.2f}")
  895. else:
  896. append_report_line(lines, f" - 实际需风量:输入 {input_qd},应为 {correct_qd:.2f}")
  897. # 添加建议
  898. append_report_line(lines, f" 建议措施:")
  899. suggestions_count = 0
  900. if not all_valid:
  901. append_report_line(lines, f" - 请根据上述不通过项修正输入参数")
  902. suggestions_count += 1
  903. if not can_calculate:
  904. append_report_line(lines, f" - 请补充必要的计算参数(体积、功率、温差等)")
  905. suggestions_count += 1
  906. if not (is_min_pass and is_max_pass):
  907. append_report_line(lines, f" - 风速验算不通过,请调整硐室供风量")
  908. suggestions_count += 1
  909. if not has_calc_check:
  910. append_report_line(lines, f" - 建议补充风量计算核验过程")
  911. suggestions_count += 1
  912. if suggestions_count == 0:
  913. append_report_line(lines, f" - 硐室风量计算正确,符合要求")
  914. result = {
  915. 'name': name,
  916. 'type': chamber_type,
  917. 'all_valid': all_valid,
  918. 'qd_correct': correct_qd,
  919. 'wind_check_pass': is_min_pass and is_max_pass,
  920. 'has_calc_check': has_calc_check,
  921. 'calculation_method': calculation_method
  922. }
  923. return result, "".join(lines)