info_model_turbine_v3.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. import pymysql
  2. import threading
  3. from typing import List, Dict, Any, Optional, Tuple
  4. import logging
  5. from datetime import datetime
  6. from collections import Counter
  7. import statistics
  8. import math
  9. # 配置日志
  10. logging.basicConfig(
  11. level=logging.INFO,
  12. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
  13. )
  14. logger = logging.getLogger(__name__)
  15. class ConnectionPool:
  16. """MySQL数据库连接池"""
  17. def __init__(self, host, port, user, password, database, charset='utf8mb4',
  18. max_connections=2, mem_quota=4 << 30):
  19. self.host = host
  20. self.port = port
  21. self.user = user
  22. self.password = password
  23. self.database = database
  24. self.charset = charset
  25. self.max_connections = max_connections
  26. self.mem_quota = mem_quota
  27. self._lock = threading.Lock()
  28. self._connections = []
  29. self._in_use = set()
  30. def _create_connection(self):
  31. """创建新连接并设置内存配额"""
  32. try:
  33. conn = pymysql.connect(
  34. host=self.host,
  35. port=self.port,
  36. user=self.user,
  37. password=self.password,
  38. database=self.database,
  39. charset=self.charset,
  40. cursorclass=pymysql.cursors.DictCursor
  41. )
  42. # 设置会话内存配额
  43. with conn.cursor() as cursor:
  44. cursor.execute(f"SET SESSION tidb_mem_quota_query = {self.mem_quota}")
  45. conn.commit()
  46. logger.debug(f"创建新数据库连接,设置内存配额为 {self.mem_quota}")
  47. return conn
  48. except Exception as e:
  49. logger.error(f"创建数据库连接失败: {e}")
  50. raise
  51. def get_connection(self):
  52. """从连接池获取连接"""
  53. with self._lock:
  54. # 如果有空闲连接,返回一个
  55. for conn in self._connections:
  56. if conn not in self._in_use:
  57. self._in_use.add(conn)
  58. logger.debug(f"从连接池获取现有连接")
  59. return conn
  60. # 如果没有空闲连接但可以创建新连接
  61. if len(self._connections) < self.max_connections:
  62. conn = self._create_connection()
  63. self._connections.append(conn)
  64. self._in_use.add(conn)
  65. logger.debug(f"创建新连接,当前连接数: {len(self._connections)}")
  66. return conn
  67. # 连接池已满,等待
  68. logger.warning("连接池已满,等待可用连接...")
  69. import time
  70. time.sleep(1)
  71. return self.get_connection()
  72. def release_connection(self, conn):
  73. """释放连接回连接池"""
  74. with self._lock:
  75. if conn in self._in_use:
  76. self._in_use.remove(conn)
  77. logger.debug(f"释放连接回连接池")
  78. def close_all(self):
  79. """关闭所有连接"""
  80. with self._lock:
  81. for conn in self._connections:
  82. try:
  83. conn.close()
  84. except:
  85. pass
  86. self._connections.clear()
  87. self._in_use.clear()
  88. logger.info("已关闭所有数据库连接")
  89. class DatabaseConfig:
  90. """数据库配置类"""
  91. def __init__(self, host='192.168.50.234', port=4000, user='root',
  92. password='123456', database='wind_data', charset='utf8mb4',
  93. max_connections=2, mem_quota=4 << 30):
  94. self.host = host
  95. self.port = port
  96. self.user = user
  97. self.password = password
  98. self.database = database
  99. self.charset = charset
  100. self.max_connections = max_connections
  101. self.mem_quota = mem_quota
  102. class ManufacturerInfo:
  103. """主机厂商信息库,用于合理性校验"""
  104. # 各厂商典型传动比范围(基于公开数据)
  105. TRANSMISSION_RATIOS = {
  106. # 直驱风机:传动比接近1
  107. "金风科技": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  108. "远景能源": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  109. "明阳智能": {"type": "半直驱", "ratio_range": (30.0, 50.0)},
  110. "运达股份": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  111. "上海电气": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  112. "东方电气": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  113. "华锐风电": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  114. "联合动力": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  115. "海装风电": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  116. "Vestas": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  117. "GE": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  118. "Siemens": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  119. }
  120. # 各容量等级典型叶轮转速范围(rpm)
  121. ROTOR_SPEED_RANGES = {
  122. 1500: (15.0, 20.0), # 1.5MW
  123. 2000: (14.0, 18.0), # 2.0MW
  124. 2500: (13.0, 17.0), # 2.5MW
  125. 3000: (12.0, 16.0), # 3.0MW
  126. 3500: (11.0, 15.0), # 3.5MW
  127. 4000: (10.0, 14.0), # 4.0MW
  128. 5000: (9.0, 13.0), # 5.0MW
  129. 6000: (8.0, 12.0), # 6.0MW
  130. }
  131. # 各类型风机典型发电机转速范围(rpm)
  132. GENERATOR_SPEED_RANGES = {
  133. "直驱": (10.0, 30.0), # 直驱:低速发电机
  134. "双馈": (1000.0, 1800.0), # 双馈:接近同步转速
  135. "半直驱": (300.0, 800.0), # 半直驱:中速发电机
  136. }
  137. @classmethod
  138. def get_manufacturer_info(cls, manufacturer: str) -> Dict:
  139. """获取厂商信息"""
  140. manufacturer_lower = str(manufacturer).strip().lower() if manufacturer else ""
  141. for key, value in cls.TRANSMISSION_RATIOS.items():
  142. if key.lower() in manufacturer_lower:
  143. return value
  144. return {"type": "未知", "ratio_range": (0.5, 200.0)}
  145. @classmethod
  146. def get_rotor_speed_range(cls, rated_capacity: int) -> Tuple[float, float]:
  147. """根据额定容量获取合理的叶轮转速范围"""
  148. if not rated_capacity:
  149. return (8.0, 25.0)
  150. # 找到最接近的容量等级
  151. capacities = sorted(cls.ROTOR_SPEED_RANGES.keys())
  152. closest_capacity = min(capacities, key=lambda x: abs(x - rated_capacity))
  153. return cls.ROTOR_SPEED_RANGES.get(closest_capacity, (8.0, 25.0))
  154. @classmethod
  155. def get_generator_speed_range(cls, turbine_type: str) -> Tuple[float, float]:
  156. """根据风机类型获取合理的发电机转速范围"""
  157. return cls.GENERATOR_SPEED_RANGES.get(turbine_type, (0.0, 2000.0))
  158. class SCADADataProcessor:
  159. """SCADA数据处理类,用于计算额定转速和传动比"""
  160. @staticmethod
  161. def calculate_robust_mode(values: List[float], decimal_places: int = 1) -> float:
  162. """
  163. 计算稳健的众数(使用核密度估计)
  164. Args:
  165. values: 数值列表
  166. decimal_places: 保留的小数位数
  167. Returns:
  168. 众数值
  169. """
  170. if not values:
  171. return 0.0
  172. # 1. 数据清洗:移除异常值(使用IQR方法)
  173. if len(values) >= 10:
  174. q1, q3 = np.percentile(values, [25, 75])
  175. iqr = q3 - q1
  176. lower_bound = q1 - 1.5 * iqr
  177. upper_bound = q3 + 1.5 * iqr
  178. filtered_values = [v for v in values if lower_bound <= v <= upper_bound]
  179. if filtered_values:
  180. values = filtered_values
  181. # 2. 对值进行四舍五入处理
  182. rounded_values = [round(v, decimal_places) for v in values]
  183. # 3. 使用Counter统计频率
  184. counter = Counter(rounded_values)
  185. if not counter:
  186. return 0.0
  187. # 4. 找到最高频率
  188. max_count = max(counter.values())
  189. # 5. 获取所有具有最高频率的值
  190. modes = [value for value, count in counter.items() if count == max_count]
  191. # 6. 如果有多个众数,返回平均值
  192. if len(modes) > 1:
  193. mode_value = sum(modes) / len(modes)
  194. logger.debug(f"多个众数: {modes}, 使用平均值: {mode_value}")
  195. else:
  196. mode_value = modes[0]
  197. logger.debug(f"众数统计: 值={mode_value}, 频次={max_count}, 总数据点={len(values)}")
  198. return mode_value
  199. @staticmethod
  200. def calculate_rated_parameters_with_validation(
  201. rotor_speeds: List[float],
  202. gen_speeds: List[float],
  203. manufacturer: str,
  204. rated_capacity: int
  205. ) -> Tuple[float, float, float, str]:
  206. """
  207. 计算额定参数并进行合理性校验
  208. Args:
  209. rotor_speeds: 叶轮转速列表
  210. gen_speeds: 发电机转速列表
  211. manufacturer: 制造商
  212. rated_capacity: 额定容量(kW)
  213. Returns:
  214. Tuple[float, float, float, str]: (rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type)
  215. """
  216. if not rotor_speeds or not gen_speeds:
  217. logger.warning(f"数据不足: 转子转速点={len(rotor_speeds)}, 发电机转速点={len(gen_speeds)}")
  218. return 0.0, 0.0, 0.0, "未知"
  219. # 获取厂商信息
  220. manu_info = ManufacturerInfo.get_manufacturer_info(manufacturer)
  221. expected_type = manu_info["type"]
  222. expected_ratio_range = manu_info["ratio_range"]
  223. # 获取合理的转速范围
  224. rotor_range = ManufacturerInfo.get_rotor_speed_range(rated_capacity)
  225. gen_range = ManufacturerInfo.get_generator_speed_range(expected_type)
  226. logger.debug(f"厂商: {manufacturer}, 预期类型: {expected_type}, "
  227. f"传动比范围: {expected_ratio_range}, "
  228. f"叶轮转速范围: {rotor_range}, 发电机转速范围: {gen_range}")
  229. # 第一阶段:初始计算
  230. # 叶轮转速:保留1位小数
  231. rated_rotor_spd = round(SCADADataProcessor.calculate_robust_mode(rotor_speeds, 1), 1)
  232. # 发电机转速:先计算众数,然后取整
  233. gen_mode = SCADADataProcessor.calculate_robust_mode(gen_speeds, 1)
  234. rated_gen_spd = int(round(gen_mode))
  235. # 计算传动比:保留3位小数
  236. if rated_rotor_spd > 0:
  237. transmission_ratio = round(rated_gen_spd / rated_rotor_spd, 3)
  238. else:
  239. transmission_ratio = 0.0
  240. # 第二阶段:合理性校验与调整
  241. adjustments = []
  242. # 1. 校验叶轮转速
  243. if not (rotor_range[0] <= rated_rotor_spd <= rotor_range[1]):
  244. adjustments.append(f"叶轮转速超出范围: {rated_rotor_spd:.1f} rpm, 合理范围: {rotor_range}")
  245. # 使用中位数并四舍五入到1位小数
  246. rated_rotor_spd = round(float(statistics.median(rotor_speeds)), 1)
  247. rated_rotor_spd = max(rotor_range[0], min(rotor_range[1], rated_rotor_spd))
  248. # 2. 校验发电机转速
  249. if not (gen_range[0] <= rated_gen_spd <= gen_range[1]):
  250. adjustments.append(f"发电机转速超出范围: {rated_gen_spd} rpm, 合理范围: {gen_range}")
  251. # 使用中位数并取整
  252. gen_median = float(statistics.median(gen_speeds))
  253. rated_gen_spd = int(round(gen_median))
  254. rated_gen_spd = int(max(gen_range[0], min(gen_range[1], rated_gen_spd)))
  255. # 3. 重新计算传动比
  256. if rated_rotor_spd > 0:
  257. transmission_ratio = round(rated_gen_spd / rated_rotor_spd, 3)
  258. else:
  259. transmission_ratio = 0.0
  260. # 4. 校验传动比
  261. if not (expected_ratio_range[0] <= transmission_ratio <= expected_ratio_range[1]):
  262. adjustments.append(f"传动比超出范围: {transmission_ratio:.3f}, 合理范围: {expected_ratio_range}")
  263. # 如果传动比异常,基于预期类型调整
  264. if expected_type == "直驱":
  265. # 直驱:传动比应接近1
  266. transmission_ratio = 1.000
  267. rated_gen_spd = int(round(rated_rotor_spd))
  268. elif expected_type == "双馈":
  269. # 双馈:典型传动比90:1
  270. transmission_ratio = 90.000
  271. rated_gen_spd = int(round(rated_rotor_spd * transmission_ratio))
  272. elif expected_type == "半直驱":
  273. # 半直驱:典型传动比40:1
  274. transmission_ratio = 40.000
  275. rated_gen_spd = int(round(rated_rotor_spd * transmission_ratio))
  276. # 第三阶段:最终校验与类型判断
  277. # 基于最终传动比判断类型
  278. if transmission_ratio <= 1.2:
  279. turbine_type = "直驱"
  280. elif 1.2 < transmission_ratio <= 30:
  281. turbine_type = "半直驱"
  282. elif 30 < transmission_ratio <= 120:
  283. turbine_type = "双馈"
  284. else:
  285. turbine_type = "未知"
  286. # 如果计算的类型与预期不符,使用预期类型
  287. if expected_type != "未知" and turbine_type != expected_type:
  288. adjustments.append(f"类型不符: 计算={turbine_type}, 预期={expected_type}")
  289. turbine_type = expected_type
  290. # 记录调整信息
  291. if adjustments:
  292. logger.info(f"参数调整: {manufacturer} {rated_capacity}kW - " + "; ".join(adjustments))
  293. # 最终格式化
  294. # 叶轮转速:保留1位小数
  295. rated_rotor_spd = round(rated_rotor_spd, 1)
  296. # 发电机转速:整数
  297. rated_gen_spd = int(rated_gen_spd)
  298. # 传动比:保留3位小数
  299. transmission_ratio = round(transmission_ratio, 3)
  300. logger.debug(f"最终结果: 转子转速={rated_rotor_spd:.1f} rpm, "
  301. f"发电机转速={rated_gen_spd} rpm, "
  302. f"传动比={transmission_ratio:.3f}, "
  303. f"类型={turbine_type}")
  304. return rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type
  305. @staticmethod
  306. def calculate_rated_speeds_and_ratio(
  307. data: List[Dict[str, Any]],
  308. manufacturer: str = "",
  309. rated_capacity: int = 0
  310. ) -> Tuple[float, float, float, str]:
  311. """
  312. 计算额定叶轮转速、额定发电机转速、传动比和风机类型
  313. Args:
  314. data: SCADA数据列表
  315. manufacturer: 制造商(用于合理性校验)
  316. rated_capacity: 额定容量(用于合理性校验)
  317. Returns:
  318. Tuple[float, float, float, str]: (rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type)
  319. """
  320. if not data:
  321. return 0.0, 0.0, 0.0, "未知"
  322. # 提取数据
  323. rotor_speeds = []
  324. gen_speeds = []
  325. for record in data:
  326. rotor_spd = record.get('rotor_spd')
  327. gen_spd = record.get('gen_spd')
  328. if rotor_spd is not None and gen_spd is not None:
  329. try:
  330. rotor_val = float(rotor_spd)
  331. gen_val = float(gen_spd)
  332. # 基本数据清洗
  333. if 0 < rotor_val < 50 and 0 < gen_val < 2500:
  334. rotor_speeds.append(rotor_val)
  335. gen_speeds.append(gen_val)
  336. except (ValueError, TypeError):
  337. continue
  338. if not rotor_speeds or not gen_speeds:
  339. logger.warning(f"有效数据不足: 转子转速点={len(rotor_speeds)}, 发电机转速点={len(gen_speeds)}")
  340. return 0.0, 0.0, 0.0, "未知"
  341. # 计算额定参数
  342. return SCADADataProcessor.calculate_rated_parameters_with_validation(
  343. rotor_speeds,
  344. gen_speeds,
  345. manufacturer,
  346. rated_capacity
  347. )
  348. # SQL语句定义(保持不变)
  349. CREATE_MODEL_TURBINE_TABLE_SQL = """
  350. CREATE TABLE IF NOT EXISTS info_model_turbine (
  351. id INT AUTO_INCREMENT PRIMARY KEY,
  352. no_model VARCHAR(255) NOT NULL COMMENT '机型唯一标识',
  353. model VARCHAR(100) COMMENT '机型',
  354. manufacturer VARCHAR(100) COMMENT '制造商',
  355. rated_capacity INT COMMENT '额定容量(kW)',
  356. cut_in_wind_speed DECIMAL(5, 2) COMMENT '切入风速(m/s)',
  357. cut_out_wind_speed DECIMAL(5, 2) COMMENT '切出风速(m/s)',
  358. rotor_diameter INT COMMENT '叶轮直径(m)',
  359. hub_height DECIMAL(10, 2) COMMENT '轮毂高度(m)',
  360. turbine_count INT DEFAULT 0 COMMENT '该机型风机数量',
  361. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  362. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  363. UNIQUE KEY idx_no_model (no_model),
  364. INDEX idx_model (model),
  365. INDEX idx_manufacturer (manufacturer),
  366. INDEX idx_rated_capacity (rated_capacity)
  367. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='风机机型信息表';
  368. """
  369. ALTER_MODEL_TURBINE_TABLE_SQL = """
  370. ALTER TABLE info_model_turbine
  371. ADD COLUMN IF NOT EXISTS rated_rotor_spd DECIMAL(10, 1) COMMENT '额定叶轮转速(rpm)',
  372. ADD COLUMN IF NOT EXISTS rated_gen_spd INT COMMENT '额定发电机转速(rpm)',
  373. ADD COLUMN IF NOT EXISTS transmission_ratio DECIMAL(10, 3) COMMENT '传动比',
  374. ADD COLUMN IF NOT EXISTS turbine_type VARCHAR(20) COMMENT '风机类型(直驱/双馈/半直驱)',
  375. ADD COLUMN IF NOT EXISTS calculation_time TIMESTAMP COMMENT '参数计算时间',
  376. ADD COLUMN IF NOT EXISTS data_points INT DEFAULT 0 COMMENT '用于计算的数据点数',
  377. ADD COLUMN IF NOT EXISTS calculation_status VARCHAR(20) DEFAULT 'pending' COMMENT '计算状态(pending/success/error/adjusted)',
  378. ADD COLUMN IF NOT EXISTS validation_info TEXT COMMENT '合理性校验信息';
  379. """
  380. UPDATE_MODEL_PARAMETERS_SQL = """
  381. UPDATE info_model_turbine
  382. SET rated_rotor_spd = %s,
  383. rated_gen_spd = %s,
  384. transmission_ratio = %s,
  385. turbine_type = %s,
  386. calculation_time = %s,
  387. data_points = %s,
  388. calculation_status = %s,
  389. validation_info = %s,
  390. updated_at = CURRENT_TIMESTAMP
  391. WHERE no_model = %s
  392. """
  393. class ModelTurbineManager:
  394. """风机机型信息管理器"""
  395. def __init__(self, db_config: DatabaseConfig):
  396. self.db_config = db_config
  397. self.connection_pool = None
  398. self._initialize_connection_pool()
  399. def _initialize_connection_pool(self):
  400. """初始化数据库连接池"""
  401. self.connection_pool = ConnectionPool(
  402. host=self.db_config.host,
  403. port=self.db_config.port,
  404. user=self.db_config.user,
  405. password=self.db_config.password,
  406. database=self.db_config.database,
  407. charset=self.db_config.charset,
  408. max_connections=self.db_config.max_connections,
  409. mem_quota=self.db_config.mem_quota
  410. )
  411. logger.info(f"数据库连接池初始化完成,最大连接数: {self.db_config.max_connections}")
  412. # ... 其他方法保持不变 ...
  413. def calculate_and_update_parameters_for_model(self, no_model: str) -> Dict[str, Any]:
  414. """
  415. 计算并更新指定机型的额定参数
  416. Args:
  417. no_model: 机型标识
  418. Returns:
  419. 计算结果的字典
  420. """
  421. try:
  422. # 先获取机型基本信息
  423. model_info_sql = """
  424. SELECT manufacturer, rated_capacity
  425. FROM info_model_turbine
  426. WHERE no_model = %s
  427. """
  428. model_info = self.execute_query(model_info_sql, (no_model,))
  429. if not model_info:
  430. logger.warning(f"机型 {no_model}: 未找到基本信息")
  431. return {
  432. "no_model": no_model,
  433. "success": False,
  434. "reason": "未找到机型基本信息",
  435. "data_points": 0
  436. }
  437. manufacturer = model_info[0].get('manufacturer', '')
  438. rated_capacity = model_info[0].get('rated_capacity', 0)
  439. # 获取该机型的SCADA数据
  440. scada_data = self.get_scada_data_for_no_model(no_model)
  441. if not scada_data:
  442. logger.warning(f"机型 {no_model}: 没有可用于计算的SCADA数据")
  443. return {
  444. "no_model": no_model,
  445. "success": False,
  446. "reason": "无SCADA数据",
  447. "data_points": 0
  448. }
  449. # 计算额定参数(带合理性校验)
  450. rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type = \
  451. SCADADataProcessor.calculate_rated_speeds_and_ratio(
  452. scada_data, manufacturer, rated_capacity
  453. )
  454. # 判断计算状态
  455. if rated_rotor_spd <= 0 or transmission_ratio <= 0:
  456. calculation_status = "error"
  457. validation_info = "参数无效"
  458. success = False
  459. else:
  460. # 检查是否需要调整
  461. manu_info = ManufacturerInfo.get_manufacturer_info(manufacturer)
  462. expected_ratio_range = manu_info["ratio_range"]
  463. rotor_range = ManufacturerInfo.get_rotor_speed_range(rated_capacity)
  464. gen_range = ManufacturerInfo.get_generator_speed_range(turbine_type)
  465. # 判断是否经过调整
  466. adjustments = []
  467. if not (rotor_range[0] <= rated_rotor_spd <= rotor_range[1]):
  468. adjustments.append(f"叶轮转速调整: {rated_rotor_spd:.1f} rpm")
  469. if not (gen_range[0] <= rated_gen_spd <= gen_range[1]):
  470. adjustments.append(f"发电机转速调整: {rated_gen_spd} rpm")
  471. if not (expected_ratio_range[0] <= transmission_ratio <= expected_ratio_range[1]):
  472. adjustments.append(f"传动比调整: {transmission_ratio:.3f}")
  473. if adjustments:
  474. calculation_status = "adjusted"
  475. validation_info = "; ".join(adjustments)
  476. else:
  477. calculation_status = "success"
  478. validation_info = "参数合理"
  479. success = True
  480. # 更新数据库
  481. update_result = self.execute_update(
  482. UPDATE_MODEL_PARAMETERS_SQL,
  483. (
  484. rated_rotor_spd,
  485. rated_gen_spd,
  486. transmission_ratio,
  487. turbine_type,
  488. datetime.now(),
  489. len(scada_data),
  490. calculation_status,
  491. validation_info,
  492. no_model
  493. )
  494. )
  495. if update_result > 0:
  496. log_msg = (f"机型 {no_model}: {calculation_status} - "
  497. f"叶轮转速={rated_rotor_spd:.1f} rpm, "
  498. f"发电机转速={rated_gen_spd} rpm, "
  499. f"传动比={transmission_ratio:.3f}, "
  500. f"类型={turbine_type}, "
  501. f"数据点={len(scada_data)}")
  502. if calculation_status == "adjusted":
  503. logger.info(log_msg + f", 调整: {validation_info}")
  504. else:
  505. logger.info(log_msg)
  506. return {
  507. "no_model": no_model,
  508. "success": success,
  509. "status": calculation_status,
  510. "rated_rotor_spd": rated_rotor_spd,
  511. "rated_gen_spd": rated_gen_spd,
  512. "transmission_ratio": transmission_ratio,
  513. "turbine_type": turbine_type,
  514. "data_points": len(scada_data),
  515. "validation_info": validation_info
  516. }
  517. else:
  518. logger.warning(f"机型 {no_model}: 数据库更新失败")
  519. return {
  520. "no_model": no_model,
  521. "success": False,
  522. "reason": "数据库更新失败",
  523. "status": "error",
  524. "rated_rotor_spd": rated_rotor_spd,
  525. "rated_gen_spd": rated_gen_spd,
  526. "transmission_ratio": transmission_ratio,
  527. "turbine_type": turbine_type,
  528. "data_points": len(scada_data)
  529. }
  530. except Exception as e:
  531. logger.error(f"计算机型 {no_model} 参数时出错: {e}")
  532. return {
  533. "no_model": no_model,
  534. "success": False,
  535. "reason": str(e),
  536. "status": "error",
  537. "data_points": 0
  538. }
  539. # ... 其他方法保持不变 ...
  540. def main():
  541. """主函数"""
  542. # 数据库配置
  543. db_config = DatabaseConfig(
  544. # host="106.120.102.238",
  545. # port=44000,
  546. host="192.168.50.234",
  547. port=4000,
  548. user='root',
  549. password='123456',
  550. database='wind_data',
  551. charset='utf8mb4',
  552. max_connections=2,
  553. mem_quota=4 << 30 # 4GB
  554. )
  555. # 创建管理器实例
  556. manager = ModelTurbineManager(db_config)
  557. # 运行机型数据提取和参数计算流程
  558. success = manager.run_model_extraction_pipeline(
  559. recreate_table=False, # 是否重新创建表
  560. calculate_params=True # 是否计算额定参数
  561. )
  562. if success:
  563. logger.info("风机机型数据提取和参数计算成功完成!")
  564. else:
  565. logger.error("风机机型数据提取和参数计算失败!")
  566. if __name__ == "__main__":
  567. # 导入numpy用于统计计算
  568. try:
  569. import numpy as np
  570. except ImportError:
  571. logger.warning("未找到numpy库,使用简单统计方法")
  572. # 定义简单的替代函数
  573. def percentile(data, percentiles):
  574. """简单的百分位数计算"""
  575. if not data:
  576. return [0, 0]
  577. sorted_data = sorted(data)
  578. n = len(sorted_data)
  579. return [
  580. sorted_data[int((n-1) * p / 100)] for p in percentiles
  581. ]
  582. # 临时创建numpy模块
  583. class NumpyStub:
  584. @staticmethod
  585. def percentile(data, percentiles):
  586. return percentile(data, percentiles)
  587. np = NumpyStub()
  588. main()