scan_stations.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. r"""场站目录扫描辨识 —— 看观澜系统在 data/raw 下认出了什么 (2026-09-11 用户令)。
  4. 约定: **`data/raw/` 的下一级目录就是「场站名称」**, 其下五个约定子目录是摄入接口:
  5. data/raw/<场站名称>/scada_10min/ SCADA 10min 导出 (逐台 WTG*.csv)
  6. data/raw/<场站名称>/scada_1min/ (可选) 1min 导出
  7. data/raw/<场站名称>/故障报警/ 报警事件导出 (SpreadsheetML *.xls)
  8. data/raw/<场站名称>/风机故障记录/ 检修工单台账 (*.xls/xlsx)
  9. data/raw/<场站名称>/油样报告/ 油液化验报告 (*.pdf)
  10. 辨识依据 (逐条命中即止): ① 场配置 raw_station 全等 → ② 与场名写法(srg_farm_names)互为子串 →
  11. ③ 只有一个场站目录 (单站部署) → ④ 都不过 = 不猜, 报"未识别"。
  12. 用法:
  13. python scripts/scan_stations.py # 扫 data/raw, 打印辨识结论与各子目录件数
  14. python scripts/scan_stations.py --json # 机器可读
  15. python scripts/scan_stations.py --root D:\\别的raw
  16. """
  17. from __future__ import annotations
  18. import argparse
  19. import json
  20. import pathlib
  21. import sys
  22. ROOT = pathlib.Path(__file__).resolve().parents[1]
  23. sys.path.insert(0, str(ROOT))
  24. def main() -> int:
  25. ap = argparse.ArgumentParser()
  26. ap.add_argument('--root', default=None, help='覆盖 data/raw 根 (默认取场配置)')
  27. ap.add_argument('--json', action='store_true')
  28. a = ap.parse_args()
  29. from src.windscada import config as C
  30. from src.windscada.config import farm, station_report, STATION_SUBDIRS
  31. cfg = farm()
  32. rep = station_report(cfg, a.root)
  33. if a.json:
  34. print(json.dumps({k: (str(v) if isinstance(v, pathlib.Path) else v)
  35. for k, v in rep.items() if k != 'stations'}
  36. | {'stations': {k: dict(n_files=v['n_files'],
  37. dir=str(v['dir']),
  38. subdirs=sorted(v['subdirs']))
  39. for k, v in rep['stations'].items()}},
  40. ensure_ascii=False, indent=2))
  41. return 0
  42. print(f'原始件根 : {pathlib.Path(a.root) if a.root else C.RAW_ROOT}')
  43. print(f'当前场 : {cfg["name"]} ({C.current()}) raw_station={cfg.get("raw_station")!r} '
  44. f'场名写法={cfg.get("src_farm_names")}')
  45. print(f'\n扫描到 {len(rep["stations"])} 个场站目录:')
  46. for nm, info in rep['stations'].items():
  47. mark = '← 本场' if rep['matched'] and info['dir'] == rep['matched'] else ''
  48. print(f' {nm} ({info["n_files"]} 件) {info["dir"]} {mark}')
  49. for sd, p in info['subdirs'].items():
  50. n = sum(1 for x in p.rglob('*') if x.is_file())
  51. print(f' {sd:14s} {n:6d} 件')
  52. print(f'\n辨识结论 : how={rep["how"]} → {rep["matched"] or "(未识别)"}')
  53. print(f'依据 : {rep["note"]}')
  54. if rep['matched']:
  55. print('\n各约定子目录件数:')
  56. for n in STATION_SUBDIRS:
  57. d = pathlib.Path(rep['matched']) / n
  58. print(f' {n:14s} {rep["counts"].get(n, 0):6d} 件 {"存在" if d.is_dir() else "缺"}')
  59. print('\n下一步: 放入/更新数据后跑 python scripts/rebuild_from_raw.py (等价验收加 --verify)')
  60. return 0
  61. if __name__ == '__main__':
  62. # 控制台 GBK 编不出 ✔ 时降级为 '?' (见 src/console.py)。
  63. from src import console
  64. console.soft()
  65. sys.exit(main())