vav.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # -*- coding: utf-8 -*-
  2. from typing import Dict, List, Tuple
  3. import numpy as np
  4. from fastapi import HTTPException
  5. from httpx import AsyncClient
  6. from loguru import logger
  7. from sqlalchemy.orm import Session
  8. from app.controllers.equipment.controller import EquipmentController
  9. from app.crud.space.weight import get_weights_by_vav
  10. from app.schemas.equipment import VAVBox, FCU
  11. from app.schemas.sapce_weight import SpaceWeight
  12. from app.schemas.space import Space
  13. from app.services.platform import DataPlatformService
  14. from app.services.transfer import Duoduo, SpaceInfoService, Season
  15. from app.utils.date import get_time_str
  16. class VAVController(EquipmentController):
  17. def __init__(self, equipment: VAVBox):
  18. super(VAVController, self).__init__()
  19. self.equipment = equipment
  20. async def get_strategy(self):
  21. strategy = 'Plan A'
  22. for space in self.equipment.spaces:
  23. for eq in space.equipment:
  24. if isinstance(eq, FCU):
  25. strategy = 'Plan B'
  26. break
  27. return strategy
  28. async def build_virtual_temperature(self) -> Tuple[float, float]:
  29. target_list, realtime_list = [], []
  30. buffer_list = []
  31. strategy = await self.get_strategy()
  32. for space in self.equipment.spaces:
  33. if not np.isnan(space.temperature_target):
  34. target_list.append(space.temperature_target)
  35. realtime_list.append(space.realtime_temperature)
  36. if strategy == 'Plan B':
  37. for eq in space.equipment:
  38. if isinstance(eq, FCU):
  39. buffer = (4 - eq.air_valve_speed) / 4
  40. buffer_list.append(buffer)
  41. break
  42. total_target = buffer_list + target_list
  43. total_realtime = buffer_list + realtime_list
  44. if total_target and total_realtime:
  45. target_result = np.array(total_target).sum() / len(target_list)
  46. realtime_result = np.array(total_realtime).sum() / len(realtime_list)
  47. self.equipment.setting_temperature = target_result
  48. else:
  49. target_result, realtime_result = np.NAN, np.NAN
  50. return target_result, realtime_result
  51. async def get_supply_air_flow_set(self, temperature_set: float, temperature_realtime: float) -> float:
  52. if np.isnan(temperature_set) or np.isnan(temperature_realtime):
  53. supply_air_flow_set = 0.0
  54. else:
  55. temperature_supply = self.equipment.supply_air_temperature
  56. if np.isnan(temperature_supply):
  57. temperature_supply = 19.0
  58. ratio = abs(1 + (temperature_realtime - temperature_set) / (temperature_set - temperature_supply))
  59. supply_air_flow_set = self.equipment.supply_air_flow * ratio
  60. supply_air_flow_set = max(self.equipment.supply_air_flow_lower_limit, supply_air_flow_set)
  61. supply_air_flow_set = min(self.equipment.supply_air_flow_upper_limit, supply_air_flow_set)
  62. self.equipment.supply_air_flow_set = supply_air_flow_set
  63. self.equipment.virtual_target_temperature = temperature_set
  64. self.equipment.virtual_realtime_temperature = temperature_realtime
  65. return supply_air_flow_set
  66. async def run(self):
  67. temperature_set, temperature_realtime = await self.build_virtual_temperature()
  68. await self.get_supply_air_flow_set(temperature_set, temperature_realtime)
  69. self.equipment.running_status = True
  70. def get_results(self):
  71. return self.equipment
  72. class VAVControllerV2(VAVController):
  73. def __init__(self, equipment: VAVBox, weights: List[SpaceWeight], season: Season):
  74. super(VAVControllerV2, self).__init__(equipment)
  75. # self.equipment = equipment
  76. self.weights = weights
  77. self.season = season
  78. async def build_virtual_temperature(self):
  79. valid_spaces = []
  80. weights = []
  81. for sp in self.equipment.spaces:
  82. if sp.realtime_temperature > 0.0 and sp.temperature_target > 0.0:
  83. valid_spaces.append(sp)
  84. for weight in self.weights:
  85. if weight.space_id == sp.id:
  86. weights.append(weight)
  87. if valid_spaces:
  88. weights = sorted(weights, key=lambda x: x.temporary_weight_update_time)
  89. if weights[-1].temporary_weight_update_time > get_time_str(60 * 60 * 2, flag='ago'):
  90. weight_dic = {weight.space_id: 0.0 for weight in weights}
  91. weight_dic.update({weights[-1].space_id: weights[-1].temporary_weight})
  92. else:
  93. weight_dic = {weight.space_id: weight.default_weight for weight in weights}
  94. total_weight_value = 0.0
  95. for v in weight_dic.values():
  96. total_weight_value += v
  97. if total_weight_value > 0:
  98. weight_dic = {k: v / total_weight_value for k, v in weight_dic.items()}
  99. else:
  100. weight_dic.update({list(weight_dic.keys())[0]: 1.0})
  101. try:
  102. virtual_target, virtual_realtime = 0.0, 0.0
  103. for sp in valid_spaces:
  104. virtual_target += sp.temperature_target * weight_dic.get(sp.id)
  105. virtual_realtime += sp.realtime_temperature * weight_dic.get(sp.id)
  106. except KeyError:
  107. logger.error(f'{self.equipment.id} has wrong vav-space relation')
  108. raise HTTPException(status_code=404, detail='This VAV box has wrong eq-sp relation')
  109. self.equipment.virtual_target_temperature = virtual_target
  110. self.equipment.virtual_realtime_temperature = virtual_realtime
  111. else:
  112. self.equipment.virtual_target_temperature = np.NAN
  113. self.equipment.virtual_realtime_temperature = np.NAN
  114. async def rectify(self) -> Tuple[float, float]:
  115. for sp in self.equipment.spaces:
  116. if sp.realtime_temperature < min(23.0, sp.temperature_target):
  117. if self.season == Season.heating:
  118. self.equipment.virtual_target_temperature = min(23.0, sp.temperature_target) + 0.5
  119. self.equipment.virtual_realtime_temperature = sp.realtime_temperature
  120. break
  121. elif sp.realtime_temperature > max(27.0, sp.temperature_target):
  122. if self.season == Season.cooling:
  123. self.equipment.virtual_target_temperature = max(27.0, sp.temperature_target)
  124. self.equipment.virtual_realtime_temperature = sp.realtime_temperature
  125. break
  126. return self.equipment.virtual_target_temperature, self.equipment.virtual_realtime_temperature
  127. async def run(self):
  128. await self.build_virtual_temperature()
  129. temperature_set, temperature_realtime = await self.rectify()
  130. await self.get_supply_air_flow_set(temperature_set, temperature_realtime)
  131. self.equipment.running_status = True
  132. async def fetch_vav_control_params(project_id: str, equipment_id: str) -> Dict:
  133. async with AsyncClient() as client:
  134. duo_duo = Duoduo(client, project_id)
  135. platform = DataPlatformService(client, project_id)
  136. _AHU_LIST = [
  137. 'Eq1101050030846e0a94670842109f7c8d8db0d44cf5',
  138. 'Eq1101050030b6b2f1db3d6944afa71e213e0d45d565'
  139. ]
  140. season = await duo_duo.get_season()
  141. served_spaces = await duo_duo.get_space_by_equipment(equipment_id)
  142. space_objects = []
  143. realtime_supply_air_temperature_list = []
  144. for sp in served_spaces:
  145. sp_id = sp.get('id')
  146. transfer = SpaceInfoService(client, project_id, sp_id)
  147. current_target = await transfer.get_current_temperature_target()
  148. realtime_temperature = await platform.get_realtime_temperature(sp_id)
  149. related_equipment = await transfer.get_equipment()
  150. equipment_objects = []
  151. for eq in related_equipment:
  152. if eq.get('category') == 'ACATFC':
  153. speed = await platform.get_fan_speed(eq.get('id'))
  154. temp_fcu_params = {'id': eq.get('id'), 'air_valve_speed': speed}
  155. fcu = FCU(**temp_fcu_params)
  156. equipment_objects.append(fcu)
  157. elif eq.get('category') == 'ACATAH':
  158. realtime_supply_air_temperature_list.append(
  159. await platform.get_realtime_supply_air_temperature(eq.get('id'))
  160. )
  161. temp_space_params = {
  162. 'id': sp_id,
  163. 'realtime_temperature': realtime_temperature,
  164. 'equipment': equipment_objects,
  165. 'temperature_target': current_target
  166. }
  167. space = Space(**temp_space_params)
  168. space_objects.append(space)
  169. realtime_supply_air_temperature = np.array(realtime_supply_air_temperature_list).mean()
  170. realtime_supply_air_flow = await platform.get_realtime_supply_air_flow(equipment_id)
  171. lower_limit, upper_limit = await platform.get_air_flow_limit(equipment_id)
  172. vav_params = {
  173. 'id': equipment_id,
  174. 'spaces': space_objects,
  175. 'supply_air_temperature': realtime_supply_air_temperature,
  176. 'supply_air_flow': realtime_supply_air_flow,
  177. 'supply_air_flow_lower_limit': lower_limit,
  178. 'supply_air_flow_upper_limit': upper_limit,
  179. 'season': season
  180. }
  181. return vav_params
  182. @logger.catch()
  183. async def get_vav_control_v1(project: str, equipment_id: str) -> VAVBox:
  184. vav_params = await fetch_vav_control_params(project, equipment_id)
  185. vav = VAVBox(**vav_params)
  186. vav_controller = VAVController(vav)
  187. await vav_controller.run()
  188. regulated_vav = vav_controller.get_results()
  189. return regulated_vav
  190. @logger.catch()
  191. async def get_vav_control_v2(db: Session, project_id: str, equipment_id: str) -> VAVBox:
  192. vav_params = await fetch_vav_control_params(project_id, equipment_id)
  193. vav = VAVBox(**vav_params)
  194. weights = get_weights_by_vav(db, equipment_id)
  195. vav_controller = VAVControllerV2(vav, [SpaceWeight.from_orm(weight) for weight in weights], vav_params['season'])
  196. await vav_controller.run()
  197. regulated_vav = vav_controller.get_results()
  198. return regulated_vav