supply_air_temperature_set.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. from typing import List
  2. import arrow
  3. import numpy as np
  4. from httpx import AsyncClient
  5. from loguru import logger
  6. from app.controllers.equipment.ahu.thermal_mode import count_vav_box_weight, fetch_status_params
  7. from app.models.domain.devices import ThermalMode
  8. from app.schemas.equipment import VAVBox
  9. from app.services.platform import DataPlatformService, InfoCode
  10. from app.services.transfer import Duoduo, Season
  11. from app.services.weather import WeatherService
  12. from app.utils.date import get_time_str, TIME_FMT
  13. class ACATAHSupplyAirTemperatureController:
  14. """
  15. Supply air temperature setting logic version 2 by WuXu.
  16. """
  17. def __init__(
  18. self,
  19. vav_boxes_list: List[VAVBox],
  20. current: float,
  21. return_air: float,
  22. thermal_mode: ThermalMode,
  23. is_off_to_on: bool,
  24. is_thermal_mode_switched: bool,
  25. season: Season
  26. ):
  27. super(ACATAHSupplyAirTemperatureController, self).__init__()
  28. self.vav_boxes_list = vav_boxes_list
  29. self.current = current
  30. self.return_air = return_air
  31. self.thermal_mode = thermal_mode
  32. self.is_off_to_on = is_off_to_on
  33. self.is_thermal_mode_switched = is_thermal_mode_switched
  34. self.season = season
  35. def calculate_by_cold_vav(self, cold_ratio: float) -> float:
  36. if self.thermal_mode == ThermalMode.cooling:
  37. if cold_ratio < 0.3:
  38. new = self.current - 1.0
  39. elif cold_ratio < 0.45:
  40. new = self.current - 0.5
  41. elif cold_ratio <= 0.55:
  42. new = self.current
  43. elif cold_ratio <= 0.7:
  44. new = self.current + 1.0
  45. elif cold_ratio <= 1.0:
  46. new = self.current + 1.5
  47. else:
  48. new = self.current
  49. elif self.thermal_mode == ThermalMode.heating:
  50. if cold_ratio < 0.3:
  51. new = self.return_air
  52. elif cold_ratio < 0.45:
  53. new = self.current - 1.0
  54. elif cold_ratio <= 0.55:
  55. new = self.current
  56. elif cold_ratio <= 0.7:
  57. new = self.current + 0.5
  58. elif cold_ratio <= 1.0:
  59. new = self.current + 1.0
  60. else:
  61. new = self.current
  62. else:
  63. new = self.current
  64. return new
  65. def get_cold_ratio(self):
  66. cold, total = 0, 0
  67. for box in self.vav_boxes_list:
  68. temp = count_vav_box_weight(
  69. box.virtual_realtime_temperature,
  70. box.virtual_target_temperature,
  71. box.supply_air_flow_upper_limit,
  72. box.supply_air_flow_lower_limit,
  73. box.supply_air_flow_set,
  74. box.valve_opening,
  75. self.season
  76. )
  77. cold += temp if temp < 0 else 0
  78. total += abs(temp)
  79. try:
  80. cold_ratio = abs(cold / total)
  81. except ZeroDivisionError:
  82. cold_ratio = np.NAN
  83. logger.debug(f'cold ratio: {cold_ratio}')
  84. return cold_ratio
  85. def build(self) -> float:
  86. if not self.is_off_to_on:
  87. cold_ratio = self.get_cold_ratio()
  88. temperature = self.calculate_by_cold_vav(cold_ratio)
  89. else:
  90. if self.season == Season.heating:
  91. temperature = 27.0
  92. elif self.season == Season.cooling:
  93. temperature = 20.0
  94. else:
  95. temperature = 25.0
  96. if self.season == Season.heating:
  97. temperature = max(20.0, min(30.0, temperature))
  98. else:
  99. temperature = max(18.0, min(30.0, temperature))
  100. return temperature
  101. class ACATAHSupplyAirTemperatureDefaultController:
  102. """
  103. Determine supply air temperature when missing data.
  104. """
  105. def __init__(self, is_clear_day: bool):
  106. super(ACATAHSupplyAirTemperatureDefaultController, self).__init__()
  107. self.is_clear_day = is_clear_day
  108. def build(self) -> float:
  109. now = get_time_str()
  110. now_time_str = arrow.get(now, TIME_FMT).time().strftime('%H%M%S')
  111. if '080000' <= now_time_str < '100000':
  112. is_morning = True
  113. else:
  114. is_morning = False
  115. if is_morning:
  116. temperature = 27.0
  117. else:
  118. if self.is_clear_day:
  119. temperature = 23.0
  120. else:
  121. temperature = 25.0
  122. return temperature
  123. async def get_planned(project_id: str, device_id: str) -> float:
  124. vav_boxes_params = await fetch_status_params(project_id, device_id)
  125. vav_boxes_lit = vav_boxes_params['vav_boxes_list']
  126. async with AsyncClient() as client:
  127. platform = DataPlatformService(client, project_id)
  128. duoduo = Duoduo(client, project_id)
  129. current_supply_air_temperature = await platform.get_realtime_supply_air_temperature(device_id)
  130. return_air_temperature = await platform.query_realtime_return_air_temperature(device_id)
  131. hot_water_valve_opening_set_duration = await platform.get_duration(
  132. InfoCode.hot_water_valve_opening_set, device_id, 15 * 60
  133. )
  134. chill_water_valve_opening_set_duration = await platform.get_duration(
  135. InfoCode.chill_water_valve_opening_set, device_id, 15 * 60
  136. )
  137. on_off_set_duration = await platform.get_duration(InfoCode.equip_switch_set, device_id, 20 * 60)
  138. season = await duoduo.get_season()
  139. # if hot_water_valve_opening_set_duration[-1]['value'] == 0.0:
  140. # thermal_mode = ThermalMode.cooling
  141. if chill_water_valve_opening_set_duration[-1]['value'] == 0.0:
  142. thermal_mode = ThermalMode.heating
  143. else:
  144. thermal_mode = ThermalMode.cooling
  145. is_off_to_on = False
  146. if on_off_set_duration[-1]['value'] == 1.0:
  147. for item in on_off_set_duration[::-1]:
  148. if item['value'] == 0.0:
  149. is_off_to_on = True
  150. break
  151. # logger.debug(f'{device_id} was off to on: {is_off_to_on}')
  152. is_thermal_mode_switched = False
  153. if len(set([item['value'] for item in hot_water_valve_opening_set_duration])) > 1:
  154. is_thermal_mode_switched = True
  155. if len(set([item['value'] for item in chill_water_valve_opening_set_duration])) > 1:
  156. is_thermal_mode_switched = True
  157. controller = ACATAHSupplyAirTemperatureController(
  158. vav_boxes_lit,
  159. current_supply_air_temperature,
  160. return_air_temperature,
  161. thermal_mode,
  162. is_off_to_on,
  163. is_thermal_mode_switched,
  164. season
  165. )
  166. next_supply_air_temperature_set = controller.build()
  167. return next_supply_air_temperature_set
  168. async def get_default(project_id: str) -> float:
  169. async with AsyncClient() as client:
  170. weather_service = WeatherService(client)
  171. realtime_weather = await weather_service.get_realtime_weather(project_id)
  172. if realtime_weather.get('text') == '晴':
  173. is_clear_day = True
  174. else:
  175. is_clear_day = False
  176. controller = ACATAHSupplyAirTemperatureDefaultController(is_clear_day)
  177. next_supply_air_temperature_set = controller.build()
  178. return next_supply_air_temperature_set
  179. @logger.catch()
  180. async def get_next_supply_air_temperature_set(project_id: str, device_id: str) -> float:
  181. try:
  182. new = await get_planned(project_id, device_id)
  183. except KeyError and IndexError:
  184. new = await get_default(project_id)
  185. logger.debug(f'next supply air temperature set: {device_id} - {new}')
  186. return new