info_model_turbine_v3.py 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327
  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. # 配置日志
  9. logging.basicConfig(
  10. level=logging.INFO,
  11. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
  12. )
  13. logger = logging.getLogger(__name__)
  14. class ConnectionPool:
  15. """MySQL数据库连接池"""
  16. def __init__(self, host, port, user, password, database, charset='utf8mb4',
  17. max_connections=2, mem_quota=4 << 30):
  18. self.host = host
  19. self.port = port
  20. self.user = user
  21. self.password = password
  22. self.database = database
  23. self.charset = charset
  24. self.max_connections = max_connections
  25. self.mem_quota = mem_quota
  26. self._lock = threading.Lock()
  27. self._connections = []
  28. self._in_use = set()
  29. def _create_connection(self):
  30. """创建新连接并设置内存配额"""
  31. try:
  32. conn = pymysql.connect(
  33. host=self.host,
  34. port=self.port,
  35. user=self.user,
  36. password=self.password,
  37. database=self.database,
  38. charset=self.charset,
  39. cursorclass=pymysql.cursors.DictCursor
  40. )
  41. # 设置会话内存配额
  42. with conn.cursor() as cursor:
  43. cursor.execute(f"SET SESSION tidb_mem_quota_query = {self.mem_quota}")
  44. conn.commit()
  45. logger.debug(f"创建新数据库连接,设置内存配额为 {self.mem_quota}")
  46. return conn
  47. except Exception as e:
  48. logger.error(f"创建数据库连接失败: {e}")
  49. raise
  50. def get_connection(self):
  51. """从连接池获取连接"""
  52. with self._lock:
  53. # 如果有空闲连接,返回一个
  54. for conn in self._connections:
  55. if conn not in self._in_use:
  56. self._in_use.add(conn)
  57. logger.debug(f"从连接池获取现有连接")
  58. return conn
  59. # 如果没有空闲连接但可以创建新连接
  60. if len(self._connections) < self.max_connections:
  61. conn = self._create_connection()
  62. self._connections.append(conn)
  63. self._in_use.add(conn)
  64. logger.debug(f"创建新连接,当前连接数: {len(self._connections)}")
  65. return conn
  66. # 连接池已满,等待
  67. logger.warning("连接池已满,等待可用连接...")
  68. import time
  69. time.sleep(1)
  70. return self.get_connection()
  71. def release_connection(self, conn):
  72. """释放连接回连接池"""
  73. with self._lock:
  74. if conn in self._in_use:
  75. self._in_use.remove(conn)
  76. logger.debug(f"释放连接回连接池")
  77. def close_all(self):
  78. """关闭所有连接"""
  79. with self._lock:
  80. for conn in self._connections:
  81. try:
  82. conn.close()
  83. except:
  84. pass
  85. self._connections.clear()
  86. self._in_use.clear()
  87. logger.info("已关闭所有数据库连接")
  88. class DatabaseConfig:
  89. """数据库配置类"""
  90. def __init__(self, host='192.168.50.234', port=4000, user='root',
  91. password='123456', database='wind_data', charset='utf8mb4',
  92. max_connections=2, mem_quota=4 << 30):
  93. self.host = host
  94. self.port = port
  95. self.user = user
  96. self.password = password
  97. self.database = database
  98. self.charset = charset
  99. self.max_connections = max_connections
  100. self.mem_quota = mem_quota
  101. class ManufacturerInfo:
  102. """主机厂商信息库,用于合理性校验"""
  103. # 各厂商典型传动比范围(基于公开数据)
  104. TRANSMISSION_RATIOS = {
  105. # 直驱风机:传动比接近1
  106. "金风科技": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  107. "远景能源": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  108. "明阳智能": {"type": "半直驱", "ratio_range": (30.0, 50.0)},
  109. "运达股份": {"type": "双馈", "ratio_range": (70.0, 100.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. "Vestas": {"type": "双馈", "ratio_range": (70.0, 100.0)},
  116. "GE": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  117. "Siemens": {"type": "直驱", "ratio_range": (0.95, 1.05)},
  118. }
  119. # 各容量等级典型叶轮转速范围(rpm)
  120. ROTOR_SPEED_RANGES = {
  121. 1500: (15.0, 20.0), # 1.5MW
  122. 2000: (14.0, 18.0), # 2.0MW
  123. 2500: (13.0, 17.0), # 2.5MW
  124. 3000: (12.0, 16.0), # 3.0MW
  125. 3500: (11.0, 15.0), # 3.5MW
  126. 4000: (10.0, 14.0), # 4.0MW
  127. 5000: (9.0, 13.0), # 5.0MW
  128. 6000: (8.0, 12.0), # 6.0MW
  129. }
  130. # 各类型风机典型发电机转速范围(rpm)
  131. GENERATOR_SPEED_RANGES = {
  132. "直驱": (10.0, 30.0), # 直驱:低速发电机
  133. "双馈": (1000.0, 1800.0), # 双馈:接近同步转速
  134. "半直驱": (300.0, 800.0), # 半直驱:中速发电机
  135. }
  136. @classmethod
  137. def get_manufacturer_info(cls, manufacturer: str) -> Dict:
  138. """获取厂商信息"""
  139. manufacturer_lower = str(manufacturer).strip().lower() if manufacturer else ""
  140. for key, value in cls.TRANSMISSION_RATIOS.items():
  141. if key.lower() in manufacturer_lower:
  142. return value
  143. return {"type": "未知", "ratio_range": (0.5, 200.0)}
  144. @classmethod
  145. def get_rotor_speed_range(cls, rated_capacity: int) -> Tuple[float, float]:
  146. """根据额定容量获取合理的叶轮转速范围"""
  147. if not rated_capacity:
  148. return (8.0, 25.0)
  149. # 找到最接近的容量等级
  150. capacities = sorted(cls.ROTOR_SPEED_RANGES.keys())
  151. closest_capacity = min(capacities, key=lambda x: abs(x - rated_capacity))
  152. return cls.ROTOR_SPEED_RANGES.get(closest_capacity, (8.0, 25.0))
  153. @classmethod
  154. def get_generator_speed_range(cls, turbine_type: str) -> Tuple[float, float]:
  155. """根据风机类型获取合理的发电机转速范围"""
  156. return cls.GENERATOR_SPEED_RANGES.get(turbine_type, (0.0, 2000.0))
  157. class SCADADataProcessor:
  158. """SCADA数据处理类,用于计算额定转速和传动比"""
  159. @staticmethod
  160. def calculate_robust_mode(values: List[float], decimal_places: int = 1) -> float:
  161. """
  162. 计算稳健的众数(使用核密度估计)
  163. Args:
  164. values: 数值列表
  165. decimal_places: 保留的小数位数
  166. Returns:
  167. 众数值
  168. """
  169. if not values:
  170. return 0.0
  171. # 1. 数据清洗:移除异常值(使用IQR方法)
  172. if len(values) >= 10:
  173. q1, q3 = np.percentile(values, [25, 75])
  174. iqr = q3 - q1
  175. lower_bound = q1 - 1.5 * iqr
  176. upper_bound = q3 + 1.5 * iqr
  177. filtered_values = [v for v in values if lower_bound <= v <= upper_bound]
  178. if filtered_values:
  179. values = filtered_values
  180. # 2. 对值进行四舍五入处理
  181. rounded_values = [round(v, decimal_places) for v in values]
  182. # 3. 使用Counter统计频率
  183. counter = Counter(rounded_values)
  184. if not counter:
  185. return 0.0
  186. # 4. 找到最高频率
  187. max_count = max(counter.values())
  188. # 5. 获取所有具有最高频率的值
  189. modes = [value for value, count in counter.items() if count == max_count]
  190. # 6. 如果有多个众数,返回平均值
  191. if len(modes) > 1:
  192. mode_value = sum(modes) / len(modes)
  193. logger.debug(f"多个众数: {modes}, 使用平均值: {mode_value}")
  194. else:
  195. mode_value = modes[0]
  196. logger.debug(f"众数统计: 值={mode_value}, 频次={max_count}, 总数据点={len(values)}")
  197. return mode_value
  198. @staticmethod
  199. def calculate_rated_parameters_with_validation(
  200. rotor_speeds: List[float],
  201. gen_speeds: List[float],
  202. manufacturer: str,
  203. rated_capacity: int
  204. ) -> Tuple[float, float, float, str]:
  205. """
  206. 计算额定参数并进行合理性校验
  207. Args:
  208. rotor_speeds: 叶轮转速列表
  209. gen_speeds: 发电机转速列表
  210. manufacturer: 制造商
  211. rated_capacity: 额定容量(kW)
  212. Returns:
  213. Tuple[float, float, float, str]: (rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type)
  214. """
  215. if not rotor_speeds or not gen_speeds:
  216. logger.warning(f"数据不足: 转子转速点={len(rotor_speeds)}, 发电机转速点={len(gen_speeds)}")
  217. return 0.0, 0.0, 0.0, "未知"
  218. # 获取厂商信息
  219. manu_info = ManufacturerInfo.get_manufacturer_info(manufacturer)
  220. expected_type = manu_info["type"]
  221. expected_ratio_range = manu_info["ratio_range"]
  222. # 获取合理的转速范围
  223. rotor_range = ManufacturerInfo.get_rotor_speed_range(rated_capacity)
  224. gen_range = ManufacturerInfo.get_generator_speed_range(expected_type)
  225. logger.debug(f"厂商: {manufacturer}, 预期类型: {expected_type}, "
  226. f"传动比范围: {expected_ratio_range}, "
  227. f"叶轮转速范围: {rotor_range}, 发电机转速范围: {gen_range}")
  228. # 第一阶段:初始计算
  229. # 叶轮转速:保留1位小数
  230. rated_rotor_spd = round(SCADADataProcessor.calculate_robust_mode(rotor_speeds, 1), 1)
  231. # 发电机转速:先计算众数,然后取整
  232. gen_mode = SCADADataProcessor.calculate_robust_mode(gen_speeds, 1)
  233. rated_gen_spd = int(round(gen_mode))
  234. # 计算传动比:保留3位小数
  235. if rated_rotor_spd > 0:
  236. transmission_ratio = round(rated_gen_spd / rated_rotor_spd, 3)
  237. else:
  238. transmission_ratio = 0.0
  239. # 第二阶段:合理性校验与调整
  240. adjustments = []
  241. # 1. 校验叶轮转速
  242. if not (rotor_range[0] <= rated_rotor_spd <= rotor_range[1]):
  243. adjustments.append(f"叶轮转速超出范围: {rated_rotor_spd:.1f} rpm, 合理范围: {rotor_range}")
  244. # 使用中位数并四舍五入到1位小数
  245. rated_rotor_spd = round(float(statistics.median(rotor_speeds)), 1)
  246. rated_rotor_spd = max(rotor_range[0], min(rotor_range[1], rated_rotor_spd))
  247. # 2. 校验发电机转速
  248. if not (gen_range[0] <= rated_gen_spd <= gen_range[1]):
  249. adjustments.append(f"发电机转速超出范围: {rated_gen_spd} rpm, 合理范围: {gen_range}")
  250. # 使用中位数并取整
  251. gen_median = float(statistics.median(gen_speeds))
  252. rated_gen_spd = int(round(gen_median))
  253. rated_gen_spd = int(max(gen_range[0], min(gen_range[1], rated_gen_spd)))
  254. # 3. 重新计算传动比
  255. if rated_rotor_spd > 0:
  256. transmission_ratio = round(rated_gen_spd / rated_rotor_spd, 3)
  257. else:
  258. transmission_ratio = 0.0
  259. # 4. 校验传动比
  260. if not (expected_ratio_range[0] <= transmission_ratio <= expected_ratio_range[1]):
  261. adjustments.append(f"传动比超出范围: {transmission_ratio:.3f}, 合理范围: {expected_ratio_range}")
  262. # 如果传动比异常,基于预期类型调整
  263. if expected_type == "直驱":
  264. # 直驱:传动比应接近1
  265. transmission_ratio = 1.000
  266. rated_gen_spd = int(round(rated_rotor_spd))
  267. elif expected_type == "双馈":
  268. # 双馈:典型传动比90:1
  269. transmission_ratio = 90.000
  270. rated_gen_spd = int(round(rated_rotor_spd * transmission_ratio))
  271. elif expected_type == "半直驱":
  272. # 半直驱:典型传动比40:1
  273. transmission_ratio = 40.000
  274. rated_gen_spd = int(round(rated_rotor_spd * transmission_ratio))
  275. # 第三阶段:最终校验与类型判断
  276. # 基于最终传动比判断类型
  277. if transmission_ratio <= 1.2:
  278. turbine_type = "直驱"
  279. elif 1.2 < transmission_ratio <= 30:
  280. turbine_type = "半直驱"
  281. elif 30 < transmission_ratio <= 120:
  282. turbine_type = "双馈"
  283. else:
  284. turbine_type = "未知"
  285. # 如果计算的类型与预期不符,使用预期类型
  286. if expected_type != "未知" and turbine_type != expected_type:
  287. adjustments.append(f"类型不符: 计算={turbine_type}, 预期={expected_type}")
  288. turbine_type = expected_type
  289. # 记录调整信息
  290. if adjustments:
  291. logger.info(f"参数调整: {manufacturer} {rated_capacity}kW - " + "; ".join(adjustments))
  292. # 最终格式化
  293. # 叶轮转速:保留1位小数
  294. rated_rotor_spd = round(rated_rotor_spd, 1)
  295. # 发电机转速:整数
  296. rated_gen_spd = int(rated_gen_spd)
  297. # 传动比:保留3位小数
  298. transmission_ratio = round(transmission_ratio, 3)
  299. logger.debug(f"最终结果: 转子转速={rated_rotor_spd:.1f} rpm, "
  300. f"发电机转速={rated_gen_spd} rpm, "
  301. f"传动比={transmission_ratio:.3f}, "
  302. f"类型={turbine_type}")
  303. return rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type
  304. @staticmethod
  305. def calculate_rated_speeds_and_ratio(
  306. data: List[Dict[str, Any]],
  307. manufacturer: str = "",
  308. rated_capacity: int = 0
  309. ) -> Tuple[float, float, float, str]:
  310. """
  311. 计算额定叶轮转速、额定发电机转速、传动比和风机类型
  312. Args:
  313. data: SCADA数据列表
  314. manufacturer: 制造商(用于合理性校验)
  315. rated_capacity: 额定容量(用于合理性校验)
  316. Returns:
  317. Tuple[float, float, float, str]: (rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type)
  318. """
  319. if not data:
  320. return 0.0, 0.0, 0.0, "未知"
  321. # 提取数据
  322. rotor_speeds = []
  323. gen_speeds = []
  324. for record in data:
  325. rotor_spd = record.get('rotor_spd')
  326. gen_spd = record.get('gen_spd')
  327. if rotor_spd is not None and gen_spd is not None:
  328. try:
  329. rotor_val = float(rotor_spd)
  330. gen_val = float(gen_spd)
  331. # 基本数据清洗
  332. if 0 < rotor_val < 50 and 0 < gen_val < 2500:
  333. rotor_speeds.append(rotor_val)
  334. gen_speeds.append(gen_val)
  335. except (ValueError, TypeError):
  336. continue
  337. if not rotor_speeds or not gen_speeds:
  338. logger.warning(f"有效数据不足: 转子转速点={len(rotor_speeds)}, 发电机转速点={len(gen_speeds)}")
  339. return 0.0, 0.0, 0.0, "未知"
  340. # 计算额定参数
  341. return SCADADataProcessor.calculate_rated_parameters_with_validation(
  342. rotor_speeds,
  343. gen_speeds,
  344. manufacturer,
  345. rated_capacity
  346. )
  347. # SQL语句定义
  348. CREATE_MODEL_TURBINE_TABLE_SQL = """
  349. CREATE TABLE IF NOT EXISTS info_model_turbine (
  350. id INT AUTO_INCREMENT PRIMARY KEY,
  351. no_model VARCHAR(255) NOT NULL COMMENT '机型唯一标识',
  352. model VARCHAR(100) COMMENT '机型',
  353. manufacturer VARCHAR(100) COMMENT '制造商',
  354. rated_capacity INT COMMENT '额定容量(kW)',
  355. cut_in_wind_speed DECIMAL(5, 2) COMMENT '切入风速(m/s)',
  356. cut_out_wind_speed DECIMAL(5, 2) COMMENT '切出风速(m/s)',
  357. rotor_diameter INT COMMENT '叶轮直径(m)',
  358. hub_height DECIMAL(10, 2) COMMENT '轮毂高度(m)',
  359. turbine_count INT DEFAULT 0 COMMENT '该机型风机数量',
  360. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  361. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  362. UNIQUE KEY idx_no_model (no_model),
  363. INDEX idx_model (model),
  364. INDEX idx_manufacturer (manufacturer),
  365. INDEX idx_rated_capacity (rated_capacity)
  366. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='风机机型信息表';
  367. """
  368. ALTER_MODEL_TURBINE_TABLE_SQL = """
  369. ALTER TABLE info_model_turbine
  370. ADD COLUMN IF NOT EXISTS rated_rotor_spd DECIMAL(10, 1) COMMENT '额定叶轮转速(rpm)',
  371. ADD COLUMN IF NOT EXISTS rated_gen_spd INT COMMENT '额定发电机转速(rpm)',
  372. ADD COLUMN IF NOT EXISTS transmission_ratio DECIMAL(10, 3) COMMENT '传动比',
  373. ADD COLUMN IF NOT EXISTS turbine_type VARCHAR(20) COMMENT '风机类型(直驱/双馈/半直驱)',
  374. ADD COLUMN IF NOT EXISTS calculation_time TIMESTAMP COMMENT '参数计算时间',
  375. ADD COLUMN IF NOT EXISTS data_points INT DEFAULT 0 COMMENT '用于计算的数据点数',
  376. ADD COLUMN IF NOT EXISTS calculation_status VARCHAR(20) DEFAULT 'pending' COMMENT '计算状态(pending/success/error/adjusted)',
  377. ADD COLUMN IF NOT EXISTS validation_info TEXT COMMENT '合理性校验信息';
  378. """
  379. UPDATE_MODEL_PARAMETERS_SQL = """
  380. UPDATE info_model_turbine
  381. SET rated_rotor_spd = %s,
  382. rated_gen_spd = %s,
  383. transmission_ratio = %s,
  384. turbine_type = %s,
  385. calculation_time = %s,
  386. data_points = %s,
  387. calculation_status = %s,
  388. validation_info = %s,
  389. updated_at = CURRENT_TIMESTAMP
  390. WHERE no_model = %s
  391. """
  392. # 其他SQL语句
  393. SELECT_MODEL_DATA_SQL = """
  394. SELECT
  395. CONCAT(
  396. IFNULL(model, ''),
  397. '-',
  398. IFNULL(cut_in_wind_speed, ''),
  399. '-',
  400. IFNULL(cut_out_wind_speed, ''),
  401. '-',
  402. IFNULL(hub_height, '')
  403. ) AS no_model,
  404. model,
  405. manufacturer,
  406. rated_capacity,
  407. cut_in_wind_speed,
  408. cut_out_wind_speed,
  409. rotor_diameter,
  410. hub_height,
  411. COUNT(*) AS turbine_count
  412. FROM info_turbine
  413. WHERE model IS NOT NULL
  414. GROUP BY model, manufacturer, rated_capacity, cut_in_wind_speed, cut_out_wind_speed, rotor_diameter, hub_height
  415. ORDER BY model, manufacturer, rated_capacity;
  416. """
  417. SELECT_NO_MODEL_LIST_SQL = """
  418. SELECT DISTINCT no_model FROM info_model_turbine ORDER BY no_model;
  419. """
  420. SELECT_SCADA_FOR_NO_MODEL_SQL = """
  421. SELECT
  422. it.wind_farm_id,
  423. it.turbine_id,
  424. it.model,
  425. icpt.rated_wind_speed,
  426. imt.no_model,
  427. imt.rated_capacity,
  428. imt.cut_in_wind_speed,
  429. imt.cut_out_wind_speed,
  430. imt.rotor_diameter,
  431. imt.hub_height,
  432. dst.data_time,
  433. dst.wind_spd,
  434. dst.rotor_spd,
  435. dst.gen_spd,
  436. dst.p_active
  437. FROM info_turbine it,
  438. info_curve_power_turbine icpt,
  439. info_model_turbine imt,
  440. data_scada_turbine dst
  441. WHERE 1=1
  442. AND it.wind_farm_id = dst.id_farm
  443. AND it.turbine_id = dst.id_turbine
  444. AND it.model = dst.no_model_turbine
  445. AND it.model = imt.model
  446. AND it.cut_in_wind_speed = imt.cut_in_wind_speed
  447. AND it.cut_out_wind_speed = imt.cut_out_wind_speed
  448. AND it.rotor_diameter = imt.rotor_diameter
  449. AND it.hub_height = imt.hub_height
  450. AND it.wind_farm_id = icpt.wind_farm_id
  451. AND it.model = icpt.standard_model
  452. AND imt.no_model = %s
  453. AND dst.wind_spd >= icpt.rated_wind_speed
  454. AND dst.p_active >= imt.rated_capacity * 0.95
  455. AND dst.p_active <= imt.rated_capacity * 1.05
  456. AND dst.rotor_spd IS NOT NULL
  457. AND dst.gen_spd IS NOT NULL
  458. AND dst.rotor_spd > 0
  459. AND dst.gen_spd > 0
  460. ORDER BY dst.data_time;
  461. """
  462. DROP_MODEL_TURBINE_TABLE_SQL = "DROP TABLE IF EXISTS info_model_turbine"
  463. CHECK_TABLE_EXISTS_SQL = """
  464. SELECT COUNT(*) as table_exists
  465. FROM information_schema.tables
  466. WHERE table_schema = %s AND table_name = 'info_model_turbine'
  467. """
  468. CHECK_TABLE_COLUMNS_SQL = """
  469. SELECT COLUMN_NAME
  470. FROM INFORMATION_SCHEMA.COLUMNS
  471. WHERE TABLE_SCHEMA = %s AND TABLE_NAME = 'info_model_turbine'
  472. """
  473. class ModelTurbineManager:
  474. """风机机型信息管理器"""
  475. def __init__(self, db_config: DatabaseConfig):
  476. self.db_config = db_config
  477. self.connection_pool = None
  478. self._initialize_connection_pool()
  479. def _initialize_connection_pool(self):
  480. """初始化数据库连接池"""
  481. self.connection_pool = ConnectionPool(
  482. host=self.db_config.host,
  483. port=self.db_config.port,
  484. user=self.db_config.user,
  485. password=self.db_config.password,
  486. database=self.db_config.database,
  487. charset=self.db_config.charset,
  488. max_connections=self.db_config.max_connections,
  489. mem_quota=self.db_config.mem_quota
  490. )
  491. logger.info(f"数据库连接池初始化完成,最大连接数: {self.db_config.max_connections}")
  492. def get_connection(self):
  493. """从连接池获取连接"""
  494. return self.connection_pool.get_connection()
  495. def release_connection(self, conn):
  496. """释放连接回连接池"""
  497. self.connection_pool.release_connection(conn)
  498. def execute_query(self, sql: str, params: Optional[tuple] = None) -> List[Dict[str, Any]]:
  499. """
  500. 执行查询语句
  501. Args:
  502. sql: SQL查询语句
  503. params: SQL参数
  504. Returns:
  505. 查询结果列表
  506. """
  507. conn = self.get_connection()
  508. if not conn:
  509. raise Exception("无法从连接池获取数据库连接")
  510. try:
  511. with conn.cursor() as cursor:
  512. cursor.execute(sql, params)
  513. result = cursor.fetchall()
  514. logger.debug(f"查询执行成功,返回 {len(result)} 条记录")
  515. return result
  516. except Exception as e:
  517. logger.error(f"查询执行失败: {e}")
  518. logger.error(f"SQL: {sql}")
  519. if params:
  520. logger.error(f"参数: {params}")
  521. raise
  522. finally:
  523. self.release_connection(conn)
  524. def execute_update(self, sql: str, params: Optional[tuple] = None) -> int:
  525. """
  526. 执行更新语句(INSERT, UPDATE, DELETE)
  527. Args:
  528. sql: SQL更新语句
  529. params: SQL参数
  530. Returns:
  531. 影响的行数
  532. """
  533. conn = self.get_connection()
  534. if not conn:
  535. raise Exception("无法从连接池获取数据库连接")
  536. try:
  537. with conn.cursor() as cursor:
  538. affected_rows = cursor.execute(sql, params)
  539. conn.commit()
  540. logger.debug(f"更新执行成功,影响 {affected_rows} 行")
  541. return affected_rows
  542. except Exception as e:
  543. conn.rollback()
  544. logger.error(f"更新执行失败: {e}")
  545. logger.error(f"SQL: {sql}")
  546. if params:
  547. logger.error(f"参数: {params}")
  548. raise
  549. finally:
  550. self.release_connection(conn)
  551. def execute_batch_update(self, sql: str, params_list: List[tuple]) -> int:
  552. """
  553. 批量执行更新语句
  554. Args:
  555. sql: SQL更新语句
  556. params_list: SQL参数列表
  557. Returns:
  558. 影响的行数
  559. """
  560. conn = self.get_connection()
  561. if not conn:
  562. raise Exception("无法从连接池获取数据库连接")
  563. try:
  564. with conn.cursor() as cursor:
  565. affected_rows = cursor.executemany(sql, params_list)
  566. conn.commit()
  567. logger.debug(f"批量更新执行成功,影响 {affected_rows} 行")
  568. return affected_rows
  569. except Exception as e:
  570. conn.rollback()
  571. logger.error(f"批量更新执行失败: {e}")
  572. logger.error(f"SQL: {sql}")
  573. raise
  574. finally:
  575. self.release_connection(conn)
  576. def check_table_exists(self, table_name: str = 'info_model_turbine') -> bool:
  577. """
  578. 检查表是否存在
  579. Args:
  580. table_name: 表名
  581. Returns:
  582. bool: 表是否存在
  583. """
  584. try:
  585. result = self.execute_query(CHECK_TABLE_EXISTS_SQL, (self.db_config.database,))
  586. return result[0]['table_exists'] > 0
  587. except Exception as e:
  588. logger.error(f"检查表存在性失败: {e}")
  589. return False
  590. def check_table_columns(self) -> List[str]:
  591. """
  592. 检查表的列
  593. Returns:
  594. List[str]: 列名列表
  595. """
  596. try:
  597. result = self.execute_query(CHECK_TABLE_COLUMNS_SQL, (self.db_config.database,))
  598. return [row['COLUMN_NAME'] for row in result]
  599. except Exception as e:
  600. logger.error(f"检查表列失败: {e}")
  601. return []
  602. def add_missing_columns(self):
  603. """添加缺失的列"""
  604. try:
  605. logger.info("检查并添加缺失的列...")
  606. self.execute_update(ALTER_MODEL_TURBINE_TABLE_SQL)
  607. logger.info("表结构更新完成")
  608. except Exception as e:
  609. logger.error(f"更新表结构失败: {e}")
  610. def create_model_turbine_table(self) -> bool:
  611. """
  612. 创建风机机型信息表
  613. Returns:
  614. bool: 是否成功创建表
  615. """
  616. try:
  617. logger.info("开始创建风机机型信息表...")
  618. self.execute_update(CREATE_MODEL_TURBINE_TABLE_SQL)
  619. logger.info("风机机型信息表创建成功")
  620. return True
  621. except Exception as e:
  622. logger.error(f"创建风机机型信息表失败: {e}")
  623. return False
  624. def drop_model_turbine_table(self) -> bool:
  625. """
  626. 删除风机机型信息表
  627. Returns:
  628. bool: 是否成功删除表
  629. """
  630. try:
  631. logger.info("开始删除风机机型信息表...")
  632. self.execute_update(DROP_MODEL_TURBINE_TABLE_SQL)
  633. logger.info("风机机型信息表删除成功")
  634. return True
  635. except Exception as e:
  636. logger.error(f"删除风机机型信息表失败: {e}")
  637. return False
  638. def get_model_data(self) -> List[Dict[str, Any]]:
  639. """
  640. 从info_turbine表获取机型分组数据
  641. Returns:
  642. 机型数据列表
  643. """
  644. try:
  645. logger.info("开始从info_turbine表查询机型数据...")
  646. data = self.execute_query(SELECT_MODEL_DATA_SQL)
  647. logger.info(f"成功查询到 {len(data)} 条机型记录")
  648. return data
  649. except Exception as e:
  650. logger.error(f"查询机型数据失败: {e}")
  651. return []
  652. def get_no_model_list(self) -> List[str]:
  653. """
  654. 获取所有no_model列表
  655. Returns:
  656. no_model列表
  657. """
  658. try:
  659. logger.info("开始查询所有机型标识...")
  660. result = self.execute_query(SELECT_NO_MODEL_LIST_SQL)
  661. no_models = [row['no_model'] for row in result]
  662. logger.info(f"成功获取 {len(no_models)} 个机型标识")
  663. return no_models
  664. except Exception as e:
  665. logger.error(f"查询机型标识列表失败: {e}")
  666. return []
  667. def get_scada_data_for_no_model(self, no_model: str) -> List[Dict[str, Any]]:
  668. """
  669. 获取指定机型的SCADA数据用于参数计算
  670. Args:
  671. no_model: 机型标识
  672. Returns:
  673. SCADA数据列表
  674. """
  675. try:
  676. logger.debug(f"开始查询机型 {no_model} 的SCADA数据...")
  677. data = self.execute_query(SELECT_SCADA_FOR_NO_MODEL_SQL, (no_model,))
  678. logger.debug(f"机型 {no_model} 查询到 {len(data)} 条SCADA记录")
  679. return data
  680. except Exception as e:
  681. logger.error(f"查询机型 {no_model} 的SCADA数据失败: {e}")
  682. return []
  683. def calculate_and_update_parameters_for_model(self, no_model: str) -> Dict[str, Any]:
  684. """
  685. 计算并更新指定机型的额定参数
  686. Args:
  687. no_model: 机型标识
  688. Returns:
  689. 计算结果的字典
  690. """
  691. try:
  692. # 先获取机型基本信息
  693. model_info_sql = """
  694. SELECT manufacturer, rated_capacity
  695. FROM info_model_turbine
  696. WHERE no_model = %s
  697. """
  698. model_info = self.execute_query(model_info_sql, (no_model,))
  699. if not model_info:
  700. logger.warning(f"机型 {no_model}: 未找到基本信息")
  701. return {
  702. "no_model": no_model,
  703. "success": False,
  704. "reason": "未找到机型基本信息",
  705. "data_points": 0
  706. }
  707. manufacturer = model_info[0].get('manufacturer', '')
  708. rated_capacity = model_info[0].get('rated_capacity', 0)
  709. # 获取该机型的SCADA数据
  710. scada_data = self.get_scada_data_for_no_model(no_model)
  711. if not scada_data:
  712. logger.warning(f"机型 {no_model}: 没有可用于计算的SCADA数据")
  713. return {
  714. "no_model": no_model,
  715. "success": False,
  716. "reason": "无SCADA数据",
  717. "data_points": 0
  718. }
  719. # 计算额定参数(带合理性校验)
  720. rated_rotor_spd, rated_gen_spd, transmission_ratio, turbine_type = \
  721. SCADADataProcessor.calculate_rated_speeds_and_ratio(
  722. scada_data, manufacturer, rated_capacity
  723. )
  724. # 判断计算状态
  725. if rated_rotor_spd <= 0 or transmission_ratio <= 0:
  726. calculation_status = "error"
  727. validation_info = "参数无效"
  728. success = False
  729. else:
  730. # 检查是否需要调整
  731. manu_info = ManufacturerInfo.get_manufacturer_info(manufacturer)
  732. expected_ratio_range = manu_info["ratio_range"]
  733. rotor_range = ManufacturerInfo.get_rotor_speed_range(rated_capacity)
  734. gen_range = ManufacturerInfo.get_generator_speed_range(turbine_type)
  735. # 判断是否经过调整
  736. adjustments = []
  737. if not (rotor_range[0] <= rated_rotor_spd <= rotor_range[1]):
  738. adjustments.append(f"叶轮转速调整: {rated_rotor_spd:.1f} rpm")
  739. if not (gen_range[0] <= rated_gen_spd <= gen_range[1]):
  740. adjustments.append(f"发电机转速调整: {rated_gen_spd} rpm")
  741. if not (expected_ratio_range[0] <= transmission_ratio <= expected_ratio_range[1]):
  742. adjustments.append(f"传动比调整: {transmission_ratio:.3f}")
  743. if adjustments:
  744. calculation_status = "adjusted"
  745. validation_info = "; ".join(adjustments)
  746. else:
  747. calculation_status = "success"
  748. validation_info = "参数合理"
  749. success = True
  750. # 更新数据库
  751. update_result = self.execute_update(
  752. UPDATE_MODEL_PARAMETERS_SQL,
  753. (
  754. rated_rotor_spd,
  755. rated_gen_spd,
  756. transmission_ratio,
  757. turbine_type,
  758. datetime.now(),
  759. len(scada_data),
  760. calculation_status,
  761. validation_info,
  762. no_model
  763. )
  764. )
  765. if update_result > 0:
  766. log_msg = (f"机型 {no_model}: {calculation_status} - "
  767. f"叶轮转速={rated_rotor_spd:.1f} rpm, "
  768. f"发电机转速={rated_gen_spd} rpm, "
  769. f"传动比={transmission_ratio:.3f}, "
  770. f"类型={turbine_type}, "
  771. f"数据点={len(scada_data)}")
  772. if calculation_status == "adjusted":
  773. logger.info(log_msg + f", 调整: {validation_info}")
  774. else:
  775. logger.info(log_msg)
  776. return {
  777. "no_model": no_model,
  778. "success": success,
  779. "status": calculation_status,
  780. "rated_rotor_spd": rated_rotor_spd,
  781. "rated_gen_spd": rated_gen_spd,
  782. "transmission_ratio": transmission_ratio,
  783. "turbine_type": turbine_type,
  784. "data_points": len(scada_data),
  785. "validation_info": validation_info
  786. }
  787. else:
  788. logger.warning(f"机型 {no_model}: 数据库更新失败")
  789. return {
  790. "no_model": no_model,
  791. "success": False,
  792. "reason": "数据库更新失败",
  793. "status": "error",
  794. "rated_rotor_spd": rated_rotor_spd,
  795. "rated_gen_spd": rated_gen_spd,
  796. "transmission_ratio": transmission_ratio,
  797. "turbine_type": turbine_type,
  798. "data_points": len(scada_data)
  799. }
  800. except Exception as e:
  801. logger.error(f"计算机型 {no_model} 参数时出错: {e}")
  802. return {
  803. "no_model": no_model,
  804. "success": False,
  805. "reason": str(e),
  806. "status": "error",
  807. "data_points": 0
  808. }
  809. def calculate_and_update_all_parameters(self) -> Dict[str, Any]:
  810. """
  811. 计算并更新所有机型的额定参数
  812. Returns:
  813. 计算统计信息
  814. """
  815. try:
  816. logger.info("开始计算并更新所有机型的额定参数...")
  817. # 获取所有机型标识
  818. no_model_list = self.get_no_model_list()
  819. if not no_model_list:
  820. logger.warning("没有找到任何机型标识")
  821. return {
  822. "total_models": 0,
  823. "success_count": 0,
  824. "failed_count": 0,
  825. "results": []
  826. }
  827. logger.info(f"共发现 {len(no_model_list)} 个机型需要计算")
  828. results = []
  829. success_count = 0
  830. failed_count = 0
  831. # 遍历每个机型进行计算
  832. for i, no_model in enumerate(no_model_list, 1):
  833. logger.info(f"处理机型 {i}/{len(no_model_list)}: {no_model}")
  834. result = self.calculate_and_update_parameters_for_model(no_model)
  835. results.append(result)
  836. if result.get("success"):
  837. success_count += 1
  838. else:
  839. failed_count += 1
  840. # 汇总统计
  841. stats = {
  842. "total_models": len(no_model_list),
  843. "success_count": success_count,
  844. "failed_count": failed_count,
  845. "success_rate": success_count / len(no_model_list) * 100 if no_model_list else 0,
  846. "results": results
  847. }
  848. logger.info(f"参数计算完成: 成功={success_count}, 失败={failed_count}, "
  849. f"成功率={stats['success_rate']:.1f}%")
  850. return stats
  851. except Exception as e:
  852. logger.error(f"计算并更新所有参数失败: {e}")
  853. raise
  854. def insert_model_data(self, model_data: List[Dict[str, Any]]) -> int:
  855. """
  856. 将机型数据插入到info_model_turbine表
  857. Args:
  858. model_data: 机型数据列表
  859. Returns:
  860. int: 成功插入的记录数
  861. """
  862. if not model_data:
  863. logger.warning("没有数据需要插入")
  864. return 0
  865. insert_sql = """
  866. INSERT INTO info_model_turbine
  867. (no_model, model, manufacturer, rated_capacity, cut_in_wind_speed,
  868. cut_out_wind_speed, rotor_diameter, hub_height, turbine_count)
  869. VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
  870. ON DUPLICATE KEY UPDATE
  871. turbine_count = VALUES(turbine_count),
  872. updated_at = CURRENT_TIMESTAMP
  873. """
  874. try:
  875. logger.info(f"开始向info_model_turbine表插入 {len(model_data)} 条记录...")
  876. # 准备插入数据
  877. insert_data = []
  878. for item in model_data:
  879. insert_data.append((
  880. item['no_model'],
  881. item['model'],
  882. item['manufacturer'],
  883. item['rated_capacity'],
  884. item['cut_in_wind_speed'],
  885. item['cut_out_wind_speed'],
  886. item['rotor_diameter'],
  887. item['hub_height'],
  888. item.get('turbine_count', 1)
  889. ))
  890. # 批量插入数据
  891. affected_rows = self.execute_batch_update(insert_sql, insert_data)
  892. logger.info(f"成功插入/更新 {affected_rows} 条记录到info_model_turbine表")
  893. return affected_rows
  894. except Exception as e:
  895. logger.error(f"插入机型数据失败: {e}")
  896. raise
  897. def get_model_turbine_stats(self) -> Dict[str, Any]:
  898. """
  899. 获取info_model_turbine表的统计信息
  900. Returns:
  901. 统计信息字典
  902. """
  903. try:
  904. # 查询统计信息
  905. stats_sql = """
  906. SELECT
  907. COUNT(*) as total_models,
  908. COUNT(DISTINCT manufacturer) as manufacturer_count,
  909. COUNT(DISTINCT model) as model_count,
  910. MIN(rated_capacity) as min_capacity,
  911. MAX(rated_capacity) as max_capacity,
  912. AVG(rated_capacity) as avg_capacity,
  913. SUM(turbine_count) as total_turbines,
  914. COUNT(CASE WHEN rated_rotor_spd IS NOT NULL AND rated_rotor_spd > 0 THEN 1 END) as calculated_models,
  915. COUNT(DISTINCT turbine_type) as turbine_type_count,
  916. SUM(data_points) as total_data_points,
  917. COUNT(CASE WHEN calculation_status = 'success' THEN 1 END) as success_count,
  918. COUNT(CASE WHEN calculation_status = 'adjusted' THEN 1 END) as adjusted_count,
  919. COUNT(CASE WHEN calculation_status = 'error' THEN 1 END) as error_count
  920. FROM info_model_turbine
  921. """
  922. result = self.execute_query(stats_sql)
  923. return result[0] if result else {}
  924. except Exception as e:
  925. logger.error(f"获取统计信息失败: {e}")
  926. return {}
  927. def get_turbine_type_distribution(self) -> List[Dict[str, Any]]:
  928. """
  929. 获取风机类型分布
  930. Returns:
  931. 类型分布列表
  932. """
  933. try:
  934. type_sql = """
  935. SELECT
  936. IFNULL(turbine_type, '未知') as turbine_type,
  937. COUNT(*) as model_count,
  938. SUM(turbine_count) as turbine_count,
  939. AVG(transmission_ratio) as avg_ratio,
  940. AVG(rated_rotor_spd) as avg_rotor_spd,
  941. AVG(rated_gen_spd) as avg_gen_spd
  942. FROM info_model_turbine
  943. WHERE turbine_type IS NOT NULL
  944. GROUP BY turbine_type
  945. ORDER BY model_count DESC
  946. """
  947. return self.execute_query(type_sql)
  948. except Exception as e:
  949. logger.error(f"获取风机类型分布失败: {e}")
  950. return []
  951. def print_model_summary(self, model_data: List[Dict[str, Any]]):
  952. """
  953. 打印机型数据摘要
  954. Args:
  955. model_data: 机型数据列表
  956. """
  957. if not model_data:
  958. logger.info("没有机型数据")
  959. return
  960. print("\n" + "="*80)
  961. print("风机机型数据摘要")
  962. print("="*80)
  963. print(f"总机型数: {len(model_data)}")
  964. # 按制造商统计
  965. manufacturers = {}
  966. for item in model_data:
  967. manufacturer = item.get('manufacturer', '未知')
  968. manufacturers[manufacturer] = manufacturers.get(manufacturer, 0) + 1
  969. print(f"制造商数: {len(manufacturers)}")
  970. print("\n制造商分布:")
  971. for manufacturer, count in sorted(manufacturers.items(), key=lambda x: x[1], reverse=True):
  972. print(f" {manufacturer}: {count} 种机型")
  973. # 按额定容量统计
  974. capacities = {}
  975. for item in model_data:
  976. capacity = item.get('rated_capacity', 0)
  977. if capacity:
  978. capacity_range = f"{capacity}kW"
  979. capacities[capacity_range] = capacities.get(capacity_range, 0) + 1
  980. print("\n额定容量分布:")
  981. for capacity, count in sorted(capacities.items(), key=lambda x: int(x[0].replace('kW', ''))):
  982. print(f" {capacity}: {count} 种机型")
  983. print("="*80)
  984. def print_calculation_summary(self, stats: Dict[str, Any]):
  985. """打印参数计算摘要"""
  986. print("\n" + "="*80)
  987. print("参数计算统计")
  988. print("="*80)
  989. print(f"总机型数: {stats.get('total_models', 0)}")
  990. print(f"成功计算的机型数: {stats.get('success_count', 0)}")
  991. print(f"失败的机型数: {stats.get('failed_count', 0)}")
  992. print(f"成功率: {stats.get('success_rate', 0):.1f}%")
  993. # 显示成功计算的部分结果
  994. success_results = [r for r in stats.get('results', []) if r.get('success')]
  995. if success_results:
  996. print(f"\n成功计算的机型示例 (前5个):")
  997. for i, result in enumerate(success_results[:5]):
  998. print(f" {i+1}. {result['no_model']}:")
  999. print(f" 叶轮转速: {result['rated_rotor_spd']:.1f} rpm")
  1000. print(f" 发电机转速: {result['rated_gen_spd']} rpm")
  1001. print(f" 传动比: {result['transmission_ratio']:.3f}")
  1002. print(f" 类型: {result['turbine_type']}")
  1003. print(f" 数据点数: {result['data_points']}")
  1004. if result.get('validation_info'):
  1005. print(f" 校验信息: {result['validation_info']}")
  1006. # 显示失败的部分原因
  1007. failed_results = [r for r in stats.get('results', []) if not r.get('success')]
  1008. if failed_results:
  1009. print(f"\n失败的机型示例 (前5个):")
  1010. for i, result in enumerate(failed_results[:5]):
  1011. print(f" {i+1}. {result['no_model']}: {result.get('reason', '未知原因')}")
  1012. print("="*80)
  1013. def run_model_extraction_pipeline(self, recreate_table: bool = False, calculate_params: bool = True) -> bool:
  1014. """
  1015. 运行完整的机型数据提取和参数计算流程
  1016. Args:
  1017. recreate_table: 是否重新创建表
  1018. calculate_params: 是否计算额定参数
  1019. Returns:
  1020. bool: 整个流程是否成功
  1021. """
  1022. try:
  1023. logger.info("开始执行风机机型数据提取和参数计算流程...")
  1024. # 步骤1: 检查或创建表
  1025. if recreate_table:
  1026. self.drop_model_turbine_table()
  1027. self.create_model_turbine_table()
  1028. else:
  1029. if not self.check_table_exists():
  1030. self.create_model_turbine_table()
  1031. else:
  1032. logger.info("info_model_turbine表已存在,将追加数据")
  1033. # 检查并添加缺失的列
  1034. self.add_missing_columns()
  1035. # 步骤2: 从info_turbine表获取机型数据
  1036. model_data = self.get_model_data()
  1037. if not model_data:
  1038. logger.error("未获取到机型数据,流程终止")
  1039. return False
  1040. # 步骤3: 打印摘要信息
  1041. self.print_model_summary(model_data)
  1042. # 步骤4: 插入数据到info_model_turbine表
  1043. inserted_count = self.insert_model_data(model_data)
  1044. # 步骤5: 计算额定参数
  1045. if calculate_params:
  1046. calculation_stats = self.calculate_and_update_all_parameters()
  1047. self.print_calculation_summary(calculation_stats)
  1048. # 步骤6: 获取并显示最终统计信息
  1049. stats = self.get_model_turbine_stats()
  1050. if stats:
  1051. print("\n数据库统计信息:")
  1052. print(f" 总机型数: {stats.get('total_models', 0)}")
  1053. print(f" 制造商数: {stats.get('manufacturer_count', 0)}")
  1054. print(f" 总风机数: {stats.get('total_turbines', 0)}")
  1055. print(f" 额定容量范围: {stats.get('min_capacity', 0)}kW - {stats.get('max_capacity', 0)}kW")
  1056. print(f" 平均额定容量: {round(stats.get('avg_capacity', 0), 1)}kW")
  1057. print(f" 已计算参数的机型数: {stats.get('calculated_models', 0)}")
  1058. print(f" 成功计算: {stats.get('success_count', 0)}, 调整后计算: {stats.get('adjusted_count', 0)}, 错误: {stats.get('error_count', 0)}")
  1059. print(f" 使用的总数据点数: {stats.get('total_data_points', 0)}")
  1060. # 显示风机类型分布
  1061. type_dist = self.get_turbine_type_distribution()
  1062. if type_dist:
  1063. print("\n风机类型分布:")
  1064. for item in type_dist:
  1065. print(f" {item['turbine_type']}: {item['model_count']} 种机型, "
  1066. f"{item['turbine_count']} 台风机")
  1067. if item['avg_ratio']:
  1068. print(f" 平均传动比: {item['avg_ratio']:.3f}, "
  1069. f"平均叶轮转速: {item['avg_rotor_spd']:.1f} rpm, "
  1070. f"平均发电机转速: {item['avg_gen_spd']:.0f} rpm")
  1071. logger.info("风机机型数据提取和参数计算流程执行完成!")
  1072. return True
  1073. except Exception as e:
  1074. logger.error(f"流程执行失败: {e}")
  1075. import traceback
  1076. traceback.print_exc()
  1077. return False
  1078. finally:
  1079. # 关闭连接池
  1080. if self.connection_pool:
  1081. self.connection_pool.close_all()
  1082. def main():
  1083. """主函数"""
  1084. # 数据库配置
  1085. db_config = DatabaseConfig(
  1086. host="192.168.50.234",
  1087. port=4000,
  1088. user='root',
  1089. password='123456',
  1090. database='wind_data',
  1091. charset='utf8mb4',
  1092. max_connections=2,
  1093. mem_quota=4 << 30 # 4GB
  1094. )
  1095. # 创建管理器实例
  1096. manager = ModelTurbineManager(db_config)
  1097. # 运行机型数据提取和参数计算流程
  1098. success = manager.run_model_extraction_pipeline(
  1099. recreate_table=False, # 是否重新创建表
  1100. calculate_params=True # 是否计算额定参数
  1101. )
  1102. if success:
  1103. logger.info("风机机型数据提取和参数计算成功完成!")
  1104. else:
  1105. logger.error("风机机型数据提取和参数计算失败!")
  1106. if __name__ == "__main__":
  1107. # 导入numpy用于统计计算
  1108. try:
  1109. import numpy as np
  1110. except ImportError:
  1111. logger.warning("未找到numpy库,使用简单统计方法")
  1112. # 定义简单的替代函数
  1113. def percentile(data, percentiles):
  1114. """简单的百分位数计算"""
  1115. if not data:
  1116. return [0, 0]
  1117. sorted_data = sorted(data)
  1118. n = len(sorted_data)
  1119. return [
  1120. sorted_data[int((n-1) * p / 100)] for p in percentiles
  1121. ]
  1122. # 临时创建numpy模块
  1123. class NumpyStub:
  1124. @staticmethod
  1125. def percentile(data, percentiles):
  1126. return percentile(data, percentiles)
  1127. np = NumpyStub()
  1128. main()