io.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. """CSV output helpers shared by the reorganized algorithms."""
  2. from __future__ import annotations
  3. import csv
  4. import re
  5. from pathlib import Path
  6. from .queries import unit_sort_key
  7. def unit_sort_key_from_unit(value): # retained for callers that only have a unit string
  8. return unit_sort_key(value)
  9. def output_sort_key(row: dict):
  10. """Keep exported records grouped by unit, then ordered by their start time."""
  11. time_field = next(
  12. (field for field in ("小时", "开始小时", "周期开始小时", "开始日期", "日期") if row.get(field)),
  13. None,
  14. )
  15. time_value = str(row.get(time_field, "")) if time_field else ""
  16. return (
  17. unit_sort_key(row.get("机组")),
  18. time_value,
  19. int(row.get("批次") or 0),
  20. str(row.get("周期编号", "")),
  21. )
  22. def write_csv(path: Path, rows: list[dict], fields: list[str], headers: dict[str, str]):
  23. path = Path(path)
  24. path.parent.mkdir(parents=True, exist_ok=True)
  25. rows = sorted(rows, key=output_sort_key)
  26. with path.open("w", encoding="utf-8-sig", newline="") as handle:
  27. writer = csv.DictWriter(handle, fieldnames=fields, extrasaction="ignore")
  28. writer.writerow({field: headers.get(field, field) for field in fields})
  29. writer.writerows(rows)
  30. def format_number(value):
  31. if value is None or value == "":
  32. return ""
  33. if isinstance(value, float):
  34. return round(value, 6)
  35. return value
  36. def clean_rows(rows):
  37. cleaned = []
  38. for row in rows:
  39. item = {}
  40. for key, value in row.items():
  41. if key.startswith("_"):
  42. continue
  43. item[key] = format_number(value)
  44. cleaned.append(item)
  45. return cleaned
  46. def write_thresholds(path: Path, configs: dict[int, dict]):
  47. rows = []
  48. for batch in sorted(configs):
  49. config = configs[batch]
  50. rows.extend([
  51. {
  52. "机组": config["unit"], "批次": batch, "点位": "YSJ_5",
  53. "点位描述": config["metadata"]["oil_description"], "单位": config["metadata"]["oil_units"],
  54. "低报警阈值": config["oil"]["low"], "低低报警阈值": config["oil"]["low_low"],
  55. "高报警阈值": config["oil"]["high"], "高高报警阈值": config["oil"]["high_high"],
  56. "低报警配置": config["alarm_sources"]["oil"]["low"],
  57. "低低报警配置": config["alarm_sources"]["oil"]["low_low"],
  58. "高报警配置": config["alarm_sources"]["oil"]["high"],
  59. "高高报警配置": config["alarm_sources"]["oil"]["high_high"],
  60. },
  61. {
  62. "机组": config["unit"], "批次": batch, "点位": "YSJ_10",
  63. "点位描述": config["metadata"]["coupling_description"], "单位": config["metadata"]["coupling_units"],
  64. "低报警阈值": "", "低低报警阈值": "",
  65. "高报警阈值": config["coupling"]["high"], "高高报警阈值": config["coupling"]["high_high"],
  66. "低报警配置": "", "低低报警配置": "",
  67. "高报警配置": config["alarm_sources"]["coupling"]["high"],
  68. "高高报警配置": config["alarm_sources"]["coupling"]["high_high"],
  69. },
  70. {
  71. "机组": config["unit"], "批次": batch, "点位": "YSJ_11",
  72. "点位描述": config["metadata"]["chain_description"], "单位": config["metadata"]["chain_units"],
  73. "低报警阈值": "", "低低报警阈值": "",
  74. "高报警阈值": config["chain"]["high"], "高高报警阈值": config["chain"]["high_high"],
  75. "低报警配置": "", "低低报警配置": "",
  76. "高报警配置": config["alarm_sources"]["chain"]["high"],
  77. "高高报警配置": config["alarm_sources"]["chain"]["high_high"],
  78. },
  79. ])
  80. fields = [
  81. "机组", "批次", "点位", "点位描述", "单位", "低报警阈值", "低低报警阈值",
  82. "高报警阈值", "高高报警阈值", "低报警配置", "低低报警配置", "高报警配置", "高高报警配置",
  83. ]
  84. write_csv(path, rows, fields, {field: field for field in fields})
  85. __all__ = [
  86. "output_sort_key", "write_csv", "format_number", "clean_rows", "write_thresholds",
  87. "unit_sort_key",
  88. ]