availability.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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. """Return {file_id: (median_span, has_samples)} from wave_sample_one."""
  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. span = float(mx) - float(mn) if mn is not None and mx is not None else 0.0
  57. result[int(row["wave_file_id"])] = (span, True)
  58. return result
  59. def main():
  60. parser = argparse.ArgumentParser(description="扫描功图周期实际存在的日期")
  61. parser.add_argument("--unit", required=True, choices=("7", "8", "9"))
  62. parser.add_argument("--cylinders", nargs="+", default=list(CYLINDERS), choices=CYLINDERS)
  63. parser.add_argument("--sides", nargs="+", default=["盖侧"], choices=SIDES)
  64. parser.add_argument("--start", required=True)
  65. parser.add_argument("--end", required=True)
  66. parser.add_argument("--per-day", type=int, default=2)
  67. args = parser.parse_args()
  68. connection = connect()
  69. try:
  70. for cylinder in args.cylinders:
  71. for side in args.sides:
  72. point = point_name(args.unit, cylinder, side)
  73. with connection.cursor() as cursor:
  74. cursor.execute(
  75. "SELECT id, sample_time FROM wave_file WHERE point_name=%s AND measurement_type='压力' "
  76. "AND sample_time >= %s AND sample_time < %s ORDER BY sample_time",
  77. (point, args.start, args.end),
  78. )
  79. files = list(cursor.fetchall())
  80. picked = sample_per_day(files, args.per_day)
  81. span_by_id = spans(connection, [int(r["id"]) for r in picked])
  82. by_day = defaultdict(list)
  83. for row in picked:
  84. by_day[row["sample_time"].date()].append(span_by_id.get(int(row["id"])))
  85. all_days = sorted(by_day)
  86. has_days = [d for d in all_days if any(v is not None for v in by_day[d])]
  87. valid_days = [d for d in all_days
  88. if np.median([v[0] for v in by_day[d] if v is not None] or [0.0]) >= MIN_SPAN]
  89. print(f"{point}: 文件 {len(files)},抽样日 {len(all_days)},"
  90. f"有 wave_sample_one 的日 {len(has_days)},其中有效周期日 {len(valid_days)}")
  91. if has_days:
  92. print(f" 有样本数据段: {has_days[0]} ~ {has_days[-1]} 共 {len(has_days)} 天")
  93. finally:
  94. connection.close()
  95. if __name__ == "__main__":
  96. main()