supply_air_temperature_set.py 7.3 KB

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