availability.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/env python3
  2. """Scan where indicator-diagram cycles actually exist (read-only).
  3. For each point it samples a few files per day *without* an rpm filter (rpm is
  4. unreliable for wave_file) and checks whether a valid first cycle exists in
  5. wave_sample_one with a real pressure span. This finds the last data segment
  6. before a fault, which may be weeks before the repair record.
  7. """
  8. from __future__ import annotations
  9. import argparse
  10. import sys
  11. from collections import defaultdict
  12. from pathlib import Path
  13. import numpy as np
  14. import pymysql
  15. BACKEND = Path(__file__).resolve().parents[1]
  16. sys.path.insert(0, str(BACKEND))
  17. from app.config import settings # noqa: E402
  18. CYLINDERS = ("一", "二", "三", "四", "五", "六")
  19. SIDES = ("盖侧", "轴侧")
  20. MIN_SPAN = 0.3
  21. def connect():
  22. return pymysql.connect(
  23. host=settings.db_host, port=settings.db_port, user=settings.db_user,
  24. password=settings.db_password, database=settings.db_name, charset="utf8mb4",
  25. cursorclass=pymysql.cursors.DictCursor, connect_timeout=8, read_timeout=300,
  26. )
  27. def point_name(unit, cylinder, side):
  28. return f"{unit}号机组{cylinder}缸压力{side}"
  29. def sample_per_day(rows, per_day):
  30. by_day = defaultdict(list)
  31. for row in rows:
  32. by_day[row["sample_time"].date()].append(row)
  33. picked = []
  34. for day in sorted(by_day):
  35. day_rows = by_day[day]
  36. if len(day_rows) <= per_day:
  37. picked.extend(day_rows)
  38. else:
  39. idx = np.linspace(0, len(day_rows) - 1, per_day).round().astype(int)
  40. picked.extend(day_rows[int(i)] for i in idx)
  41. return picked
  42. def spans(connection, file_ids):
  43. """Median pressure span of wave_sample_one per file_id."""
  44. result = {}
  45. with connection.cursor() as cursor:
  46. for start in range(0, len(file_ids), 200):
  47. chunk = file_ids[start:start + 200]
  48. ph = ", ".join(["%s"] * len(chunk))
  49. cursor.execute(
  50. f"SELECT wave_file_id, COUNT(*) n, MIN(signal_value) mn, MAX(signal_value) mx "
  51. f"FROM wave_sample_one WHERE wave_file_id IN ({ph}) GROUP BY wave_file_id",
  52. tuple(chunk),
  53. )
  54. for row in cursor.fetchall():
  55. mn, mx = row["mn"], row["mx"]
  56. result[int(row["wave_file_id"])] = float(mx) - float(mn) if mn is not None and mx is not None else 0.0
  57. return result
  58. def main():
  59. parser = argparse.ArgumentParser(description="扫描功图周期实际存在的日期")
  60. parser.add_argument("--unit", required=True, choices=("7", "8", "9"))
  61. parser.add_argument("--cylinders", nargs="+", default=list(CYLINDERS), choices=CYLINDERS)
  62. parser.add_argument("--sides", nargs="+", default=["盖侧"], choices=SIDES)
  63. parser.add_argument("--start", required=True)
  64. parser.add_argument("--end", required=True)
  65. parser.add_argument("--per-day", type=int, default=2)
  66. args = parser.parse_args()
  67. connection = connect()
  68. try:
  69. for cylinder in args.cylinders:
  70. for side in args.sides:
  71. point = point_name(args.unit, cylinder, side)
  72. with connection.cursor() as cursor:
  73. cursor.execute(
  74. "SELECT id, sample_time FROM wave_file WHERE point_name=%s AND measurement_type='压力' "
  75. "AND sample_time >= %s AND sample_time < %s ORDER BY sample_time",
  76. (point, args.start, args.end),
  77. )
  78. files = list(cursor.fetchall())
  79. picked = sample_per_day(files, args.per_day)
  80. span_by_id = spans(connection, [int(r["id"]) for r in picked])
  81. by_day = defaultdict(list)
  82. for row in picked:
  83. by_day[row["sample_time"].date()].append(span_by_id.get(int(row["id"]), 0.0))
  84. valid_days = [d for d, v in sorted(by_day.items()) if np.median(v) >= MIN_SPAN and len(span_by_id)]
  85. all_days = sorted(by_day)
  86. print(f"{point}: 文件 {len(files)},抽样日 {len(all_days)},其中有效日 {len(valid_days)}")
  87. if valid_days:
  88. print(f" 有效数据段: {valid_days[0]} ~ {valid_days[-1]} 共 {len(valid_days)} 天")
  89. print(" 有效日:", ", ".join(str(d) for d in valid_days))
  90. finally:
  91. connection.close()
  92. if __name__ == "__main__":
  93. main()