vav.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. # -*- coding: utf-8 -*-
  2. from typing import List, Tuple
  3. import numpy as np
  4. from httpx import AsyncClient
  5. from fastapi import HTTPException
  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.space import Space
  12. from app.schemas.sapce_weight import SpaceWeight
  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, weights: List[SpaceWeight], season: Season):
  18. super(VAVController, self).__init__()
  19. self._equipment = equipment
  20. self.weights = weights
  21. self.season = season
  22. async def get_strategy(self):
  23. strategy = 'Plan A'
  24. for space in self._equipment.spaces:
  25. for eq in space.equipment:
  26. if isinstance(eq, FCU):
  27. strategy = 'Plan B'
  28. break
  29. return strategy
  30. async def build_virtual_temperature_v1(self) -> Tuple[float, float]:
  31. target_list, realtime_list = [], []
  32. buffer_list = []
  33. strategy = await self.get_strategy()
  34. for space in self._equipment.spaces:
  35. if not np.isnan(space.temperature_target):
  36. target_list.append(space.temperature_target)
  37. realtime_list.append(space.realtime_temperature)
  38. if strategy == 'Plan B':
  39. for eq in space.equipment:
  40. if isinstance(eq, FCU):
  41. buffer = (4 - eq.air_valve_speed) / 4
  42. buffer_list.append(buffer)
  43. break
  44. logger.info(f'target list: {target_list}')
  45. logger.info(f'realtime list: {realtime_list}')
  46. logger.info(f'buffer list: {buffer_list}')
  47. total_target = buffer_list + target_list
  48. total_realtime = buffer_list + realtime_list
  49. if total_target and total_realtime:
  50. target_result = np.array(total_target).sum() / len(target_list)
  51. realtime_result = np.array(total_realtime).sum() / len(realtime_list)
  52. self._equipment.setting_temperature = target_result
  53. else:
  54. target_result, realtime_result = np.NAN, np.NAN
  55. return target_result, realtime_result
  56. async def build_virtual_temperature_v2(self):
  57. weights = sorted(self.weights, key=lambda weight: weight.temporary_weight_update_time)
  58. if weights[-1].temporary_weight_update_time > get_time_str(60 * 60 * 2, flag='ago'):
  59. weight_dic = {weight.id: 0.0 for weight in weights}
  60. weight_dic.update({weights[-1].id: weights[-1].temporary_weight})
  61. else:
  62. weight_dic = {weight.id: weight.default_weight for weight in weights}
  63. try:
  64. virtual_target, virtual_realtime = 0.0, 0.0
  65. for sp in self._equipment.spaces:
  66. virtual_target += sp.temperature_target * weight_dic.get(sp.id)
  67. virtual_realtime += sp.realtime_temperature * weight_dic.get(sp.id)
  68. except KeyError:
  69. logger.error(f'{self._equipment.id} has wrong vav-space relation')
  70. raise HTTPException(status_code=404, detail='This VAV box has wrong eq-sp relation')
  71. self._equipment.virtual_target_temperature = virtual_target
  72. self._equipment.virtual_realtime_temperature = virtual_realtime
  73. async def rectify(self) -> Tuple[float, float]:
  74. for sp in self._equipment.spaces:
  75. if sp.realtime_temperature < min(23.0, sp.temperature_target):
  76. if self.season == Season.heating:
  77. self._equipment.virtual_target_temperature = min(23.0, sp.temperature_target) + 0.5
  78. self._equipment.virtual_realtime_temperature = sp.realtime_temperature
  79. break
  80. elif sp.realtime_temperature > max(27.0, sp.temperature_target):
  81. if self.season == Season.cooling:
  82. self._equipment.virtual_target_temperature = max(27.0, sp.temperature_target)
  83. self._equipment.virtual_realtime_temperature = sp.realtime_temperature
  84. break
  85. return self._equipment.virtual_target_temperature, self._equipment.virtual_realtime_temperature
  86. async def get_supply_air_flow_set(self) -> float:
  87. await self.build_virtual_temperature_v2()
  88. temperature_set, temperature_realtime = await self.rectify()
  89. if np.isnan(temperature_set) or np.isnan(temperature_realtime):
  90. supply_air_flow_set = 0.0
  91. else:
  92. temperature_supply = self._equipment.supply_air_temperature
  93. if np.isnan(temperature_supply):
  94. temperature_supply = 19.0
  95. logger.info(f'supply air flow: {self._equipment.supply_air_flow}')
  96. logger.info(f'supply air temperature: {temperature_supply}')
  97. logger.info(f'set temperature: {temperature_set}')
  98. logger.info(f'realtime temperature: {temperature_realtime}')
  99. supply_air_flow_set = self._equipment.supply_air_flow * ((temperature_supply - temperature_realtime)
  100. / (temperature_supply - temperature_set))
  101. supply_air_flow_set = max(self._equipment.supply_air_flow_lower_limit, supply_air_flow_set)
  102. supply_air_flow_set = min(self._equipment.supply_air_flow_upper_limit, supply_air_flow_set)
  103. self._equipment.supply_air_flow_set = supply_air_flow_set
  104. return supply_air_flow_set
  105. async def run(self):
  106. await self.get_supply_air_flow_set()
  107. self._equipment.running_status = True
  108. def get_results(self):
  109. return self._equipment
  110. @logger.catch()
  111. async def get_vav_control_result(db: Session, project_id: str, equipment_id: str) -> VAVBox:
  112. async with AsyncClient() as client:
  113. duo_duo = Duoduo(client, project_id)
  114. platform = DataPlatformService(client, project_id)
  115. _AHU_LIST = [
  116. 'Eq1101050030846e0a94670842109f7c8d8db0d44cf5',
  117. 'Eq1101050030b6b2f1db3d6944afa71e213e0d45d565'
  118. ]
  119. realtime_supply_air_temperature_list = []
  120. for eq in _AHU_LIST:
  121. realtime_supply_air_temperature_list.append(await platform.get_realtime_supply_air_temperature(eq))
  122. realtime_supply_air_temperature = np.array(realtime_supply_air_temperature_list).mean()
  123. realtime_supply_air_flow = await platform.get_realtime_supply_air_flow(equipment_id)
  124. lower_limit, upper_limit = await platform.get_air_flow_limit(equipment_id)
  125. season = await duo_duo.get_season()
  126. served_spaces = await duo_duo.get_space_by_equipment(equipment_id)
  127. space_objects = []
  128. for sp in served_spaces:
  129. sp_id = sp.get('id')
  130. transfer = SpaceInfoService(client, project_id, sp_id)
  131. current_target = await transfer.get_current_temperature_target()
  132. realtime_temperature = await platform.get_realtime_temperature(sp_id)
  133. related_equipment = await transfer.get_equipment()
  134. equipment_objects = []
  135. for eq in related_equipment:
  136. if eq.get('category') == 'ACATFC':
  137. speed = await platform.get_fan_speed(eq.get('id'))
  138. temp_fcu_params = {'id': eq.get('id'), 'air_valve_speed': speed}
  139. fcu = FCU(**temp_fcu_params)
  140. equipment_objects.append(fcu)
  141. temp_space_params = {
  142. 'id': sp_id,
  143. 'realtime_temperature': realtime_temperature,
  144. 'equipment': equipment_objects,
  145. 'temperature_target': current_target
  146. }
  147. space = Space(**temp_space_params)
  148. space_objects.append(space)
  149. temp_vav_params = {
  150. 'id': equipment_id,
  151. 'spaces': space_objects,
  152. 'supply_air_temperature': realtime_supply_air_temperature,
  153. 'supply_air_flow': realtime_supply_air_flow,
  154. 'supply_air_flow_lower_limit': lower_limit,
  155. 'supply_air_flow_upper_limit': upper_limit,
  156. }
  157. vav = VAVBox(**temp_vav_params)
  158. weights = get_weights_by_vav(db, equipment_id)
  159. vav_controller = VAVController(vav, [SpaceWeight.from_orm(weight) for weight in weights], season)
  160. await vav_controller.run()
  161. regulated_vav = vav_controller.get_results()
  162. return regulated_vav