supply_air_temperature_set.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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.return_air + 1.0
  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. self.season
  75. )
  76. cold += temp if temp < 0 else 0
  77. total += abs(temp)
  78. try:
  79. cold_ratio = abs(cold / total)
  80. except ZeroDivisionError:
  81. cold_ratio = np.NAN
  82. logger.debug(f'cold ratio: {cold_ratio}')
  83. return cold_ratio
  84. def build(self) -> float:
  85. if not self.is_off_to_on:
  86. cold_ratio = self.get_cold_ratio()
  87. temperature = self.calculate_by_cold_vav(cold_ratio)
  88. else:
  89. if self.season == Season.heating:
  90. temperature = 27.0
  91. elif self.season == Season.cooling:
  92. temperature = 20.0
  93. else:
  94. temperature = 25.0
  95. if self.season == Season.heating:
  96. temperature = max(20.0, min(30.0, temperature))
  97. else:
  98. temperature = max(18.0, min(30.0, temperature))
  99. return temperature
  100. class ACATAHSupplyAirTemperatureDefaultController:
  101. """
  102. Determine supply air temperature when missing data.
  103. """
  104. def __init__(self, is_clear_day: bool):
  105. super(ACATAHSupplyAirTemperatureDefaultController, self).__init__()
  106. self.is_clear_day = is_clear_day
  107. def build(self) -> float:
  108. now = get_time_str()
  109. now_time_str = arrow.get(now, TIME_FMT).time().strftime('%H%M%S')
  110. if '080000' <= now_time_str < '100000':
  111. is_morning = True
  112. else:
  113. is_morning = False
  114. if is_morning:
  115. temperature = 27.0
  116. else:
  117. if self.is_clear_day:
  118. temperature = 23.0
  119. else:
  120. temperature = 25.0
  121. return temperature
  122. async def get_planned(project_id: str, device_id: str) -> float:
  123. vav_boxes_params = await fetch_status_params(project_id, device_id)
  124. vav_boxes_lit = vav_boxes_params['vav_boxes_list']
  125. async with AsyncClient() as client:
  126. platform = DataPlatformService(client, project_id)
  127. duoduo = Duoduo(client, project_id)
  128. current_supply_air_temperature = await platform.get_realtime_supply_air_temperature(device_id)
  129. return_air_temperature = await platform.query_realtime_return_air_temperature(device_id)
  130. hot_water_valve_opening_set_duration = await platform.get_duration(
  131. InfoCode.hot_water_valve_opening_set, device_id, 15 * 60
  132. )
  133. chill_water_valve_opening_set_duration = await platform.get_duration(
  134. InfoCode.chill_water_valve_opening_set, device_id, 15 * 60
  135. )
  136. on_off_set_duration = await platform.get_duration(InfoCode.equip_switch_set, device_id, 20 * 60)
  137. season = await duoduo.get_season()
  138. # if hot_water_valve_opening_set_duration[-1]['value'] == 0.0:
  139. # thermal_mode = ThermalMode.cooling
  140. if chill_water_valve_opening_set_duration[-1]['value'] == 0.0:
  141. thermal_mode = ThermalMode.heating
  142. else:
  143. thermal_mode = ThermalMode.cooling
  144. is_off_to_on = False
  145. if on_off_set_duration[-1]['value'] == 1.0:
  146. for item in on_off_set_duration[::-1]:
  147. if item['value'] == 0.0:
  148. is_off_to_on = True
  149. break
  150. # logger.debug(f'{device_id} was off to on: {is_off_to_on}')
  151. is_thermal_mode_switched = False
  152. if len(set([item['value'] for item in hot_water_valve_opening_set_duration])) > 1:
  153. is_thermal_mode_switched = True
  154. if len(set([item['value'] for item in chill_water_valve_opening_set_duration])) > 1:
  155. is_thermal_mode_switched = True
  156. controller = ACATAHSupplyAirTemperatureController(
  157. vav_boxes_lit,
  158. current_supply_air_temperature,
  159. return_air_temperature,
  160. thermal_mode,
  161. is_off_to_on,
  162. is_thermal_mode_switched,
  163. season
  164. )
  165. next_supply_air_temperature_set = controller.build()
  166. return next_supply_air_temperature_set
  167. async def get_default(project_id: str) -> float:
  168. async with AsyncClient() as client:
  169. weather_service = WeatherService(client)
  170. realtime_weather = await weather_service.get_realtime_weather(project_id)
  171. if realtime_weather.get('text') == '晴':
  172. is_clear_day = True
  173. else:
  174. is_clear_day = False
  175. controller = ACATAHSupplyAirTemperatureDefaultController(is_clear_day)
  176. next_supply_air_temperature_set = controller.build()
  177. return next_supply_air_temperature_set
  178. @logger.catch()
  179. async def get_next_supply_air_temperature_set(project_id: str, device_id: str) -> float:
  180. try:
  181. new = await get_planned(project_id, device_id)
  182. except KeyError and IndexError:
  183. new = await get_default(project_id)
  184. logger.debug(f'next supply air temperature set: {device_id} - {new}')
  185. return new