supply_air_temperature_set.py 6.9 KB

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