optimize.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. import os
  2. from datetime import datetime,timedelta
  3. from pathlib import Path
  4. from pprint import pprint
  5. from typing import Union
  6. import numpy as np
  7. import pandas as pd
  8. from ...model.DHU.DHU_AB import DHU_AB
  9. from ...model.DHU.SDHU_AB import SDHU_AB
  10. from .config_reader import ConfigReader
  11. from ...tools.data_loader import DataLoader
  12. from ..._utils.data_summary import print_dataframe,summary_dataframe
  13. def optimize(*inputs,config=None):
  14. config = {} if config is None else config
  15. if '__LOCAL' in config.keys():
  16. config_reader_path = config['__LOCAL']
  17. data_URL = config['__URL']
  18. else:
  19. config_reader_path = '/mnt/workflow_data'
  20. data_URL = 'http://basedataportal-svc:8080/data/getpointsdata'
  21. config_reader = ConfigReader(path=f'{config_reader_path}/DHU配置.xlsx')
  22. ALL_RESULT = {
  23. 'EXCEPTION':{
  24. 'State' : {},
  25. 'Mod' : {},
  26. 'Data_ATD' : {},
  27. 'Opt' : {},
  28. 'Push' : {}
  29. },
  30. 'STATUS':{
  31. 'Mode_Steady': [],
  32. 'Mode_Low' : [],
  33. 'Mode_Off' : []
  34. }
  35. }
  36. ALL_OPT_RES = []
  37. for each_eaup_name,each_eaup_name_short in zip(
  38. config_reader.all_equp_names,
  39. config_reader.all_equp_names_short
  40. ):
  41. if not config_reader.get_app_info(each_eaup_name,'实时优化','开始实时优化','bool'):
  42. continue
  43. each_equp_type = config_reader.get_equp_info(each_eaup_name,key='设备类型',info_type='str')
  44. global NOW
  45. NOW = config_reader.get_app_info(each_eaup_name,'实时优化','运行时间','datetime').replace(second=0,microsecond=0)
  46. print(f'{each_eaup_name}开始实时优化,设备类型为{each_equp_type},运行时间为{NOW}')
  47. try:
  48. dhu_State = load_dhu_State(
  49. each_eaup_name = each_eaup_name,
  50. config_reader = config_reader,
  51. config_reader_path = config_reader_path,
  52. data_URL = data_URL,
  53. NOW = NOW
  54. )
  55. print(f'{each_eaup_name}设备状态为{dhu_State}')
  56. if dhu_State < 1:
  57. print('设备处于非运行状态,跳过')
  58. ALL_RESULT['STATUS']['Mode_Off'].append(each_eaup_name)
  59. continue
  60. except Exception as e:
  61. ALL_RESULT['EXCEPTION']['State'][each_eaup_name] = e
  62. continue
  63. # 加载模型
  64. try:
  65. MODEL = load_model(
  66. each_eaup_name = each_eaup_name,
  67. each_equp_type = each_equp_type,
  68. config_reader = config_reader,
  69. config_reader_path = config_reader_path
  70. )
  71. except Exception as e:
  72. ALL_RESULT['EXCEPTION']['Mod'][each_eaup_name] = e
  73. continue
  74. try:
  75. data_dhu = load_data_dhu(
  76. each_eaup_name = each_eaup_name,
  77. each_equp_type = each_equp_type,
  78. config_reader = config_reader,
  79. config_reader_path = config_reader_path,
  80. data_URL = data_URL
  81. )
  82. except Exception as e:
  83. ALL_RESULT['EXCEPTION']['Data_ATD'][each_eaup_name] = e
  84. continue
  85. try:
  86. ALL_RESULT['STATUS']['Mode_Steady'].append(each_eaup_name)
  87. opt_result = optimize_dhu(
  88. MODEL = MODEL,
  89. data_cur = data_dhu,
  90. config_reader = config_reader,
  91. each_eaup_name = each_eaup_name,
  92. each_equp_type = each_equp_type
  93. )
  94. ALL_OPT_RES.append(opt_result['opt_summary'][1].assign(equp_name=each_eaup_name))
  95. except Exception as e:
  96. ALL_RESULT['EXCEPTION']['Opt'][each_eaup_name] = e
  97. continue
  98. try:
  99. push_result(
  100. each_eaup_name = each_eaup_name,
  101. each_eaup_name_short = each_eaup_name_short,
  102. each_equp_type = each_equp_type,
  103. config_reader = config_reader,
  104. opt_result = opt_result,
  105. data_cur = data_dhu
  106. )
  107. except Exception as e:
  108. ALL_RESULT['EXCEPTION']['Push'][each_eaup_name] = e
  109. continue
  110. pprint(ALL_RESULT)
  111. print_dataframe(pd.concat(ALL_OPT_RES,axis=0),'优化结果')
  112. def load_model(
  113. each_eaup_name,
  114. each_equp_type,
  115. config_reader:ConfigReader,
  116. config_reader_path,
  117. use_adj_name = True
  118. ):
  119. adjust_equp_name = config_reader.get_equp_info(each_eaup_name,'替代模型','str')
  120. if (adjust_equp_name in config_reader.all_equp_names_total) and use_adj_name:
  121. print(f'{each_eaup_name}使用替代模型{adjust_equp_name}')
  122. each_eaup_name = adjust_equp_name
  123. else:
  124. pass
  125. if each_equp_type in ['DHU_A','DHU_B']:
  126. MODEL = DHU_AB
  127. elif each_equp_type in ['SDHU_A','SDHU_B']:
  128. MODEL = SDHU_AB
  129. else:
  130. raise NotImplementedError
  131. if config_reader.get_app_info(
  132. each_eaup_name,
  133. app_type = '实时优化',
  134. key = '使用临时模型',
  135. info_type = 'bool'
  136. ):
  137. # 从文件夹中获取临时模型
  138. MODEL = MODEL.load(path=f'{config_reader_path}/model/{each_eaup_name}.pkl')
  139. else:
  140. MODEL = MODEL.load_from_platform(
  141. source = 'id',
  142. model_id = config_reader.get_equp_info(
  143. equp_name = each_eaup_name,
  144. key = '模型编号',
  145. info_type = 'str'
  146. )
  147. )
  148. return MODEL
  149. def load_dhu_State(
  150. each_eaup_name,
  151. config_reader,
  152. config_reader_path,
  153. data_URL,
  154. NOW,
  155. ):
  156. data_last = (
  157. DataLoader(
  158. path = f'{config_reader_path}/data/optimize/data_cur/',
  159. start_time = NOW - timedelta(minutes=1),
  160. end_time = NOW,
  161. print_process = False
  162. )
  163. .download_equp_data(
  164. equp_name = each_eaup_name,
  165. point = {'State':config_reader.get_equp_point(each_eaup_name,equp_class=['A'])['State']},
  166. url = data_URL,
  167. clean_cache = True
  168. )
  169. .get_equp_data(
  170. equp_name = each_eaup_name,
  171. )
  172. )
  173. return data_last.iat[-1,0]
  174. def load_data_room(
  175. each_eaup_name,
  176. each_equp_type,
  177. config_reader,
  178. config_reader_path,
  179. data_URL
  180. ):
  181. room_steady_len = config_reader.get_app_info(each_eaup_name,'实时优化','房间稳态判断时长','float')
  182. room_data_loader = DataLoader(
  183. path = f'{config_reader_path}/data/optimize/data_cur/',
  184. start_time = NOW - timedelta(minutes=room_steady_len),
  185. end_time = NOW,
  186. print_process = False
  187. )
  188. room_data_loader.download_equp_data(
  189. equp_name = each_eaup_name,
  190. point = config_reader.get_equp_point(each_eaup_name,equp_class=['C']),
  191. url = data_URL,
  192. clean_cache = True
  193. )
  194. room_data = room_data_loader.get_equp_data(each_eaup_name)
  195. summary_dataframe(room_data,f'{each_eaup_name}房间数据')
  196. room_Dew_SP_adj = config_reader.get_app_info(each_eaup_name,'实时优化','房间露点设定值偏差','float')
  197. room_Dew_SP = room_data.room_DSP.mean() + room_Dew_SP_adj
  198. room_Dew_PV = room_data.room_DPV.mean()
  199. room_Dew_diff_dwlim = config_reader.get_app_info(each_eaup_name,'实时优化','房间露点过低阈值','float')
  200. # 模式判断
  201. is_room_Dew_steady = (room_Dew_PV < (room_Dew_SP + 0.5)) and (room_Dew_PV > (room_Dew_SP - 0.5))
  202. is_room_Dew_low = room_Dew_PV < (room_Dew_SP - room_Dew_diff_dwlim)
  203. data_room = {
  204. 'is_room_Dew_steady': is_room_Dew_steady,
  205. 'is_room_Dew_low' : is_room_Dew_low,
  206. 'room_Dew_SP' : room_Dew_SP,
  207. 'room_Dew_PV' : room_Dew_PV
  208. }
  209. return data_room
  210. def load_data_dhu(
  211. each_eaup_name,
  212. each_equp_type,
  213. config_reader,
  214. config_reader_path,
  215. data_URL,
  216. ):
  217. dhu_steady_len = config_reader.get_app_info(each_eaup_name,'实时优化','除湿机工况均值时长','float')
  218. data_input_point = config_reader.get_equp_point(each_eaup_name,equp_class=['A','B'])
  219. data_last = (
  220. DataLoader(
  221. path = f'{config_reader_path}/data/optimize/data_cur/',
  222. start_time = NOW - timedelta(minutes=dhu_steady_len),
  223. end_time = NOW,
  224. print_process = False
  225. )
  226. .download_equp_data(
  227. equp_name = each_eaup_name,
  228. point = data_input_point,
  229. url = data_URL,
  230. clean_cache = True
  231. )
  232. .get_equp_data(
  233. equp_name = each_eaup_name,
  234. )
  235. )
  236. data_cur = data_last.mean(axis=0).to_frame().T
  237. summary_dataframe(data_last,f'{each_eaup_name}除湿机数据')
  238. return data_cur
  239. def optimize_dhu(
  240. MODEL : Union[DHU_AB,SDHU_AB],
  241. data_cur : pd.DataFrame,
  242. config_reader : ConfigReader,
  243. each_eaup_name : str,
  244. each_equp_type : str
  245. ):
  246. # 模型精度判断
  247. predict = MODEL.predict_system(input_data=data_cur)
  248. TVP_data = (
  249. predict.T.set_axis(['pred'],axis=1)
  250. .join(
  251. data_cur.T.set_axis(['real'],axis=1),
  252. how = 'left'
  253. )
  254. .dropna(axis=0)
  255. .assign(diff=lambda x: x['pred'] - x['real'])
  256. )
  257. print(TVP_data)
  258. # 约束条件
  259. constrains = []
  260. if each_equp_type in ['DHU_A','DHU_B']:
  261. constrains.append('#送风露点约束# coil_3_DoutA-[coil_3_DoutA]<0')
  262. Tdup = config_reader.get_equp_info(each_eaup_name,key='前后再生差值上限',info_type='float')
  263. Tddw = config_reader.get_equp_info(each_eaup_name,key='前后再生差值下限',info_type='float')
  264. if not np.isnan(Tdup):
  265. constrains.append(f'#前后再生差值上限# (wheel_1_TinR-wheel_2_TinR)-{Tdup}<0')
  266. if not np.isnan(Tddw):
  267. constrains.append(f'#前后再生差值下限# {Tddw}-(wheel_1_TinR-wheel_2_TinR)<0')
  268. elif each_equp_type in ['SDHU_A','SDHU_B']:
  269. constrains.append('#送风露点约束# wheel_1_DoutP-[wheel_1_DoutP]<0')
  270. print('约束条件')
  271. pprint(constrains)
  272. # 实时优化
  273. if each_equp_type in ['DHU_A','DHU_B']:
  274. opt_res = MODEL.optimize(
  275. cur_input_data=data_cur,
  276. wheel_1_TinR=(
  277. config_reader.get_equp_info(each_eaup_name,key='前再生温度上限',info_type='float'),
  278. config_reader.get_equp_info(each_eaup_name,key='前再生温度下限',info_type='float')
  279. ),
  280. wheel_2_TinR=(
  281. config_reader.get_equp_info(each_eaup_name,key='后再生温度上限',info_type='float'),
  282. config_reader.get_equp_info(each_eaup_name,key='后再生温度下限',info_type='float')
  283. ),
  284. fan_2_Hz = None,
  285. constrains = constrains,
  286. logging = False,
  287. target = 'summary_waste',
  288. target_min = True
  289. )
  290. elif each_equp_type in ['SDHU_A','SDHU_B']:
  291. opt_res = MODEL.optimize(
  292. cur_input_data = data_cur,
  293. wheel_1_TinR = (
  294. config_reader.get_equp_info(each_eaup_name,key='前再生温度上限',info_type='float'),
  295. config_reader.get_equp_info(each_eaup_name,key='前再生温度下限',info_type='float')
  296. ),
  297. fan_2_Hz = (30,45),
  298. constrains = constrains,
  299. logging = False,
  300. target = 'summary_waste',
  301. target_min = True
  302. )
  303. opt_summary = opt_res['opt_summary']
  304. opt_var = opt_res['opt_var']
  305. print_dataframe(opt_summary,'优化结果')
  306. print_dataframe(opt_var,'优化变量')
  307. return {
  308. 'opt_summary': opt_summary,
  309. 'opt_var' : opt_var,
  310. }
  311. def push_result(
  312. each_eaup_name,
  313. each_eaup_name_short,
  314. each_equp_type,
  315. config_reader:ConfigReader,
  316. opt_result:dict,
  317. data_cur:pd.DataFrame
  318. ):
  319. equp_point = config_reader.get_equp_point(each_eaup_name,equp_class=['D'])
  320. if each_equp_type in ['DHU_A','DHU_B']:
  321. rcmd = [
  322. {
  323. 'name' : '前再生加热温度',
  324. 'rcmd_value': opt_result['opt_var'][0].iat[0,0],
  325. 'children' : [
  326. {
  327. 'name' : '前再生加热温度',
  328. 'rcmd_value': opt_result['opt_var'][0].round(1).iat[0,0],
  329. 'curr_value': data_cur.loc[:,['wheel_1_TinR']].round(1).iat[0,0],
  330. 'point_id' : f'{each_eaup_name}_{equp_point["wheel_1_TinR_AISP"]}'
  331. }
  332. ]
  333. },
  334. {
  335. 'name' : '后再生加热温度',
  336. 'rcmd_value': opt_result['opt_var'][1].iat[0,0],
  337. 'children' : [
  338. {
  339. 'name' : '后再生加热温度',
  340. 'rcmd_value': opt_result['opt_var'][1].round(1).iat[0,0],
  341. 'curr_value': data_cur.loc[:,['wheel_2_TinR']].round(1).iat[0,0],
  342. 'point_id' : f'{each_eaup_name}_{equp_point["wheel_2_TinR_AISP"]}'
  343. }
  344. ]
  345. },
  346. ]
  347. elif each_equp_type in ['SDHU_A','SDHU_B']:
  348. rcmd = [
  349. {
  350. 'name' : '再生加热温度',
  351. 'rcmd_value': opt_result['opt_var'][0].iat[0,0],
  352. 'children' : [
  353. {
  354. 'name' : '再生加热温度',
  355. 'rcmd_value': opt_result['opt_var'][0].round(1).iat[0,0],
  356. 'curr_value': data_cur.loc[:,['wheel_1_TinR']].round(1).iat[0,0],
  357. 'point_id' : f'{each_eaup_name}_{equp_point["wheel_1_TinR_AISP"]}'
  358. }
  359. ]
  360. },
  361. {
  362. 'name' : '排风机频率',
  363. 'rcmd_value': opt_result['opt_var'][1].iat[0,0],
  364. 'children' : [
  365. {
  366. 'name' : '排风机频率',
  367. 'rcmd_value': opt_result['opt_var'][1].round(1).iat[0,0],
  368. 'curr_value': data_cur.loc[:,['fan_2_Hz']].round(1).iat[0,0],
  369. 'point_id' : f'{each_eaup_name}_{equp_point["fan_2_Hz_AISP"]}'
  370. }
  371. ]
  372. },
  373. ]
  374. else:
  375. raise Exception('MODEL_TYPE_ERROR')
  376. if config_reader.get_app_info(each_eaup_name,'实时优化','推送策略','bool'):
  377. add_ai_rcmd_operation(
  378. code = each_eaup_name_short,
  379. job_id = os.environ.get('JOB_ID', None),
  380. rcmd = rcmd,
  381. custom_details = []
  382. )
  383. else:
  384. pprint(rcmd)
  385. def add_ai_rcmd_operation(code,job_id,rcmd,custom_details):
  386. from workflowlib import requests
  387. json_info = {
  388. "job_id": job_id,
  389. "code" : code,
  390. "data" : {
  391. "rcmd" : rcmd,
  392. "custom_details": custom_details
  393. },
  394. }
  395. print('ai_rcmd',json_info)
  396. try:
  397. # 下发分步指令
  398. url = 'http://m2-backend-svc:8000/api/ai/ai_rcmd_operation/add_ai_rcmd_operation_v3'
  399. r = requests.post(url,json=json_info,headers={'Content-Type': "application/json", "Connection": "close"})
  400. jsonResp = r.json()
  401. print(f'下发指令 jsonResp',jsonResp)
  402. except Exception as e:
  403. print(e)
  404. return