run_segments.py 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/env python3
  2. """Machine running segments from ``wave_file.rpm > 0`` (read-only).
  3. The site's 开机停机表 is known to be inaccurate, so the running state is taken
  4. from the data itself: every calendar day that has at least one pressure record
  5. with ``rpm > 0`` is a running day. Consecutive running days (allowing a small
  6. merge gap for missing daily records) form one *running segment*. ``event_bins``
  7. uses these segments to pick the pre/post running periods instead of fixed
  8. event-relative windows.
  9. """
  10. from __future__ import annotations
  11. import argparse
  12. import csv
  13. import sys
  14. from datetime import date
  15. from pathlib import Path
  16. import pymysql
  17. BACKEND = Path(__file__).resolve().parents[1]
  18. sys.path.insert(0, str(BACKEND))
  19. from app.config import settings # noqa: E402
  20. def connect() -> pymysql.connections.Connection:
  21. return pymysql.connect(
  22. host=settings.db_host, port=settings.db_port, user=settings.db_user,
  23. password=settings.db_password, database=settings.db_name, charset="utf8mb4",
  24. cursorclass=pymysql.cursors.DictCursor, connect_timeout=8, read_timeout=120,
  25. )
  26. def running_days(unit: str, connection=None) -> list[date]:
  27. """Sorted distinct days with a running pressure record for one unit."""
  28. own = connection is None
  29. connection = connection or connect()
  30. try:
  31. with connection.cursor() as cursor:
  32. cursor.execute(
  33. """
  34. SELECT DISTINCT DATE(sample_time) AS d
  35. FROM wave_file
  36. WHERE point_name LIKE %s AND measurement_type = '压力' AND rpm > 0
  37. ORDER BY d
  38. """,
  39. (f"{unit}号机组%",),
  40. )
  41. return [row["d"] for row in cursor.fetchall()]
  42. finally:
  43. if own:
  44. connection.close()
  45. def build_segments(days: list[date], merge_gap: int = 2) -> list[tuple[date, date]]:
  46. """Merge running days into segments; a gap of at most ``merge_gap`` off-days is bridged."""
  47. segments: list[list[date]] = []
  48. for day in days:
  49. if segments and (day - segments[-1][1]).days <= merge_gap + 1:
  50. segments[-1][1] = day
  51. else:
  52. segments.append([day, day])
  53. return [(start, end) for start, end in segments]
  54. def unit_segments(unit: str, merge_gap: int = 2, connection=None) -> list[tuple[date, date]]:
  55. return build_segments(running_days(unit, connection), merge_gap)
  56. def main() -> None:
  57. parser = argparse.ArgumentParser(description="wave_file.rpm>0 统计开机日与运行段(只读)")
  58. parser.add_argument("--unit", required=True, choices=("7", "8", "9"))
  59. parser.add_argument("--merge-gap", type=int, default=2, help="允许跨过的停机天数(桥接)")
  60. parser.add_argument("--out", default="", help="运行段 CSV 输出路径(可选)")
  61. args = parser.parse_args()
  62. days = running_days(args.unit)
  63. segments = build_segments(days, args.merge_gap)
  64. print(f"{args.unit}号机 rpm>0 开机日 {len(days)} 天,运行段 {len(segments)} 个:")
  65. for start, end in segments:
  66. print(f" {start} ~ {end} ({(end - start).days + 1}天)")
  67. if args.out:
  68. out = Path(args.out)
  69. out.parent.mkdir(parents=True, exist_ok=True)
  70. with out.open("w", encoding="utf-8-sig", newline="") as handle:
  71. writer = csv.writer(handle)
  72. writer.writerow(["unit", "start", "end", "days"])
  73. for start, end in segments:
  74. writer.writerow([args.unit, start, end, (end - start).days + 1])
  75. print("->", out)
  76. if __name__ == "__main__":
  77. main()