智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. """
  2. 水务管理系统主程序
  3. 集成REST API、WebSocket和批量导入功能
  4. """
  5. import asyncio
  6. import logging
  7. import signal
  8. import sys
  9. from pathlib import Path
  10. import argparse
  11. from datetime import datetime
  12. # 导入各个模块
  13. from src.api.rest_api import app as rest_api_app
  14. from src.websocket.websocket_server import websocket_server
  15. from src.batch.batch_import import batch_manager
  16. from src.utils.data_utils import data_converter, data_formatter, quality_checker
  17. from src.models.models import validator
  18. # 配置日志
  19. logging.basicConfig(
  20. level=logging.INFO,
  21. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  22. handlers=[
  23. logging.FileHandler('water_management.log'),
  24. logging.StreamHandler(sys.stdout)
  25. ]
  26. )
  27. logger = logging.getLogger(__name__)
  28. class WaterManagementSystem:
  29. """水务管理系统主类"""
  30. def __init__(self, config_file: str = "config.json"):
  31. self.config_file = config_file
  32. self.running = False
  33. self.tasks = []
  34. self.config = self.load_config()
  35. def load_config(self) -> dict:
  36. """加载配置文件"""
  37. config_path = Path(self.config_file)
  38. if config_path.exists():
  39. import json
  40. with open(config_path, 'r', encoding='utf-8') as f:
  41. return json.load(f)
  42. else:
  43. # 默认配置
  44. return {
  45. "api": {
  46. "host": "0.0.0.0",
  47. "port": 8000
  48. },
  49. "websocket": {
  50. "host": "0.0.0.0",
  51. "port": 8765
  52. },
  53. "batch": {
  54. "max_file_size_mb": 100,
  55. "supported_formats": ["csv", "excel", "json"]
  56. },
  57. "logging": {
  58. "level": "INFO",
  59. "file": "water_management.log"
  60. }
  61. }
  62. async def start_api_server(self):
  63. """启动API服务器"""
  64. import uvicorn
  65. logger.info("启动REST API服务器...")
  66. # 在新的事件循环中运行uvicorn
  67. api_config = uvicorn.Config(
  68. app=rest_api_app,
  69. host=self.config["api"]["host"],
  70. port=self.config["api"]["port"],
  71. log_level="info"
  72. )
  73. api_server = uvicorn.Server(api_config)
  74. # 在后台任务中运行
  75. await api_server.serve()
  76. async def start_websocket_server(self):
  77. """启动WebSocket服务器"""
  78. logger.info("启动WebSocket服务器...")
  79. # 启动WebSocket服务器
  80. server = await websocket_server.start_server()
  81. # 添加服务器关闭处理
  82. def cleanup():
  83. logger.info("关闭WebSocket服务器...")
  84. server.close()
  85. asyncio.create_task(server.wait_closed())
  86. return server, cleanup
  87. async def start_data_generator(self):
  88. """启动数据生成器(用于演示)"""
  89. logger.info("启动数据生成器...")
  90. while self.running:
  91. # 生成模拟数据
  92. import random
  93. sensor_types = ["LL", "YL", "SW", "ZD"]
  94. sensor_type = random.choice(sensor_types)
  95. # 根据传感器类型生成合理的数值范围
  96. if sensor_type == "LL": # 流量
  97. value = random.uniform(10, 100)
  98. elif sensor_type == "YL": # 压力
  99. value = random.uniform(0.1, 1.0)
  100. elif sensor_type == "SW": # 水位
  101. value = random.uniform(0, 10)
  102. else: # ZD 浊度
  103. value = random.uniform(0, 50)
  104. sensor_data = {
  105. "data_type": sensor_type,
  106. "device_id": f"device_{random.randint(1, 10)}",
  107. "value": round(value, 2),
  108. "location": random.choice(["A区", "B区", "C区", "D区"])
  109. }
  110. # 通过WebSocket发送数据
  111. await websocket_server.send_sensor_data(sensor_data)
  112. # 等待5秒
  113. await asyncio.sleep(5)
  114. async def handle_batch_import(self, file_path: str, batch_id: str, data_source: str):
  115. """处理批量导入请求"""
  116. try:
  117. logger.info(f"开始批量导入: {file_path}")
  118. # 验证文件
  119. validation_result = await batch_manager.validate_file(file_path)
  120. if not validation_result["valid"]:
  121. raise Exception(f"文件验证失败: {validation_result['error']}")
  122. # 导入文件
  123. result = await batch_manager.import_file(
  124. file_path=file_path,
  125. batch_id=batch_id,
  126. data_source=data_source,
  127. file_type="auto"
  128. )
  129. logger.info(f"批量导入完成: {result}")
  130. return result
  131. except Exception as e:
  132. logger.error(f"批量导入失败: {str(e)}")
  133. raise
  134. async def start_system(self):
  135. """启动系统"""
  136. logger.info("启动水务管理系统...")
  137. # 标记系统为运行状态
  138. self.running = True
  139. try:
  140. # 启动WebSocket服务器
  141. ws_server, ws_cleanup = await self.start_websocket_server()
  142. self.tasks.append(ws_server)
  143. # 启动数据生成器(如果启用)
  144. if self.config.get("demo_mode", False):
  145. generator_task = asyncio.create_task(self.start_data_generator())
  146. self.tasks.append(generator_task)
  147. # 启动API服务器
  148. await self.start_api_server()
  149. except Exception as e:
  150. logger.error(f"系统启动失败: {str(e)}")
  151. await self.stop_system()
  152. raise
  153. async def stop_system(self):
  154. """停止系统"""
  155. logger.info("停止水务管理系统...")
  156. self.running = False
  157. # 取消所有任务
  158. for task in self.tasks:
  159. if not task.done():
  160. task.cancel()
  161. try:
  162. await task
  163. except asyncio.CancelledError:
  164. pass
  165. # 清理WebSocket服务器
  166. if hasattr(websocket_server, 'server') and websocket_server.server:
  167. websocket_server.server.close()
  168. await websocket_server.server.wait_closed()
  169. logger.info("水务管理系统已停止")
  170. async def run(self):
  171. """运行系统"""
  172. # 设置信号处理
  173. def signal_handler():
  174. logger.info("收到停止信号...")
  175. asyncio.create_task(self.stop_system())
  176. for sig in [signal.SIGINT, signal.SIGTERM]:
  177. signal.signal(sig, signal_handler)
  178. try:
  179. await self.start_system()
  180. # 保持运行直到收到停止信号
  181. while self.running:
  182. await asyncio.sleep(1)
  183. except KeyboardInterrupt:
  184. logger.info("收到键盘中断信号")
  185. except Exception as e:
  186. logger.error(f"系统运行时出错: {str(e)}")
  187. finally:
  188. await self.stop_system()
  189. def create_sample_config():
  190. """创建示例配置文件"""
  191. sample_config = {
  192. "api": {
  193. "host": "0.0.0.0",
  194. "port": 8000
  195. },
  196. "websocket": {
  197. "host": "0.0.0.0",
  198. "port": 8765
  199. },
  200. "batch": {
  201. "max_file_size_mb": 100,
  202. "supported_formats": ["csv", "excel", "json"]
  203. },
  204. "demo_mode": True,
  205. "logging": {
  206. "level": "INFO",
  207. "file": "water_management.log"
  208. }
  209. }
  210. import json
  211. with open("config.json", 'w', encoding='utf-8') as f:
  212. json.dump(sample_config, f, indent=2, ensure_ascii=False)
  213. logger.info("示例配置文件已创建: config.json")
  214. def create_requirements():
  215. """创建requirements.txt文件"""
  216. requirements = [
  217. "fastapi==0.104.1",
  218. "uvicorn[standard]==0.24.0",
  219. "websockets==12.0",
  220. "pandas==2.1.3",
  221. "openpyxl==3.1.2",
  222. "aiofiles==23.2.1",
  223. "python-multipart==0.0.6",
  224. "jinja2==3.1.2"
  225. ]
  226. with open("requirements.txt", 'w') as f:
  227. f.write('\n'.join(requirements))
  228. logger.info("依赖文件已创建: requirements.txt")
  229. def main():
  230. """主函数"""
  231. parser = argparse.ArgumentParser(description="水务管理系统")
  232. parser.add_argument("--config", "-c", default="config.json", help="配置文件路径")
  233. parser.add_argument("--init", action="store_true", help="初始化项目(创建配置文件和依赖)")
  234. parser.add_argument("--demo", action="store_true", help="启动演示模式")
  235. args = parser.parse_args()
  236. if args.init:
  237. create_sample_config()
  238. create_requirements()
  239. logger.info("项目初始化完成")
  240. return
  241. # 创建系统实例
  242. system = WaterManagementSystem(args.config)
  243. # 如果启用演示模式
  244. if args.demo:
  245. system.config["demo_mode"] = True
  246. logger.info("启用演示模式")
  247. # 运行系统
  248. try:
  249. asyncio.run(system.run())
  250. except KeyboardInterrupt:
  251. logger.info("程序已退出")
  252. if __name__ == "__main__":
  253. main()