on_ratio.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import asyncio
  2. from typing import Tuple
  3. import arrow
  4. from httpx import AsyncClient
  5. from loguru import logger
  6. from app.controllers.equipment.fcu.basic import FCUControllerV2
  7. from app.schemas.equipment import FCU
  8. from app.schemas.space import Space
  9. from app.services.platform import DataPlatformService, InfoCode
  10. from app.services.transfer import Season
  11. from app.utils.date import get_time_str, TIME_FMT
  12. from app.utils.math import round_half_up
  13. class OnRatioController:
  14. def __init__(self, target: float, return_air: float, period_num: int):
  15. super(OnRatioController, self).__init__()
  16. self.target = target
  17. self.return_air = return_air
  18. self.period_num = period_num
  19. def select_mode(self) -> str:
  20. if self.target < self.return_air:
  21. mode = 'off'
  22. else:
  23. if self.target - self.return_air > 1:
  24. mode = 'normal'
  25. else:
  26. mode = 'on_ratio'
  27. return mode
  28. def select_speed(self, delta_all: float) -> str:
  29. mode = self.select_mode()
  30. if mode == 'off':
  31. speed = 'off'
  32. elif mode == 'normal':
  33. if delta_all > 0.00088889:
  34. speed = 'medium'
  35. else:
  36. speed = 'high'
  37. else:
  38. speed = 'medium'
  39. return speed
  40. def select_water_valve(self) -> bool:
  41. mode = self.select_mode()
  42. if mode == 'off':
  43. switch = True
  44. elif mode == 'normal':
  45. switch = False
  46. else:
  47. switch = True
  48. return switch
  49. def calculate_on_ratio(self, delta_on: float, delta_off: float) -> float:
  50. if self.period_num == 0:
  51. ratio = 0.5
  52. else:
  53. if delta_on <= 0:
  54. ratio = 1.0
  55. else:
  56. try:
  57. ratio = (0.5 * (self.return_air - self.target) + delta_off) / (delta_off - delta_on)
  58. if ratio > 1:
  59. ratio = 1.0
  60. except ZeroDivisionError:
  61. ratio = 0.0
  62. return ratio
  63. async def send_instructions(device_id: str, switch: bool, speed: str, water_valve: bool) -> None:
  64. switch_value = 1.0 if switch else 0.0
  65. water_valve_value = 1.0 if water_valve else 0.0
  66. speed_value_dict = {
  67. 'off': 0.0,
  68. 'low': 1.0,
  69. 'medium': 2.0,
  70. 'high': 3.0
  71. }
  72. speed_value = speed_value_dict.get(speed)
  73. async with AsyncClient() as client:
  74. platform = DataPlatformService(client, 'Pj1101080259')
  75. await platform.set_code_value(device_id, code=InfoCode.equip_switch_set, value=switch_value)
  76. await platform.set_code_value(device_id, code=InfoCode.water_valve_switch_set, value=water_valve_value)
  77. await platform.set_code_value(device_id, code=InfoCode.fan_speed_set, value=speed_value)
  78. await platform.set_code_value(device_id, code=InfoCode.work_mode_set, value=2.0)
  79. # await platform.set_code_value(device_id, code=InfoCode.in_cloud_set, value=1.0)
  80. async def fetch_params(device_id: str, period_time: int, last_ratio: float) -> Tuple[float, float, float, float]:
  81. async with AsyncClient() as client:
  82. middle_time = int(round_half_up(period_time * (1 - last_ratio)))
  83. platform = DataPlatformService(client, 'Pj1101080259')
  84. return_air_c = await platform.get_realtime_data(InfoCode.return_air_temperature, device_id)
  85. return_air_a = await platform.get_past_data(InfoCode.return_air_temperature, device_id, period_time)
  86. return_air_b = await platform.get_past_data(InfoCode.return_air_temperature, device_id, middle_time)
  87. delta_all = (return_air_c - return_air_a) / period_time
  88. if 0 < last_ratio < 1:
  89. delta_on = (return_air_b - return_air_a) / (period_time - middle_time)
  90. delta_off = (return_air_c - return_air_b) / middle_time
  91. elif last_ratio == 0.0:
  92. delta_on = 0.0
  93. delta_off = (return_air_c - return_air_b) / middle_time
  94. else:
  95. delta_on = (return_air_b - return_air_a) / (period_time - middle_time)
  96. delta_off = 0.0
  97. return return_air_c, delta_all, delta_on, delta_off
  98. @logger.catch()
  99. async def start_on_ratio_mode(device_id: str, target: float, period_time: int):
  100. _DAILY_ON_TIME = '060000'
  101. _DAILY_OFF_TIME = '220000'
  102. period_num = 0
  103. last_on_ratio = 1.0
  104. life_count = 0
  105. while life_count < 2000:
  106. try:
  107. time_str = get_time_str()
  108. if _DAILY_ON_TIME <= arrow.get(time_str, TIME_FMT).time().strftime('%H%M%S') < _DAILY_OFF_TIME:
  109. return_air, delta_all, delta_on, delta_off = await fetch_params(device_id, period_time, last_on_ratio)
  110. controller = OnRatioController(target, return_air, period_num)
  111. mode = controller.select_mode()
  112. speed = controller.select_speed(delta_all)
  113. switch = False if speed == 'off' else True
  114. water_valve = controller.select_water_valve()
  115. if mode == 'on_ratio':
  116. on_ratio = controller.calculate_on_ratio(delta_on, delta_off)
  117. on_range = round_half_up(period_time * on_ratio)
  118. off_range = period_time - on_range
  119. logger.debug(f'on time: {on_range}, off time: {off_range}, on ratio: {on_ratio}')
  120. await send_instructions(device_id, switch, speed, water_valve)
  121. await asyncio.sleep(on_range)
  122. await send_instructions(device_id, switch, speed, False)
  123. await asyncio.sleep(off_range)
  124. period_num += 1
  125. last_on_ratio = on_ratio
  126. else:
  127. await send_instructions(device_id, switch, speed, water_valve)
  128. await asyncio.sleep(period_time)
  129. period_num = 0
  130. last_on_ratio = 1.0
  131. life_count += 1
  132. logger.info(f'{life_count}: {device_id} - {mode}')
  133. else:
  134. await send_instructions(device_id, False, 'off', False)
  135. period_num = 0
  136. last_on_ratio = 0.0
  137. await asyncio.sleep(period_time)
  138. except (KeyError, IndexError, TypeError):
  139. await asyncio.sleep(period_time)
  140. continue
  141. @logger.catch()
  142. async def start_control_group_mode(device_id: str, target: float):
  143. async with AsyncClient() as client:
  144. platform = DataPlatformService(client, 'Pj1101080259')
  145. return_air = await platform.get_realtime_data(InfoCode.return_air_temperature, device_id)
  146. space_params = {
  147. 'id': device_id,
  148. 'temperature_target': target,
  149. 'realtime_temperature': return_air
  150. }
  151. space = Space(**space_params)
  152. fcu_params = {
  153. 'id': device_id,
  154. 'space': space
  155. }
  156. fcu = FCU(**fcu_params)
  157. controller = FCUControllerV2(fcu, Season.heating)
  158. await controller.run()
  159. regulated_fcu = controller.get_results()
  160. speed_value_dict = {
  161. 0.0: 'off',
  162. 1.0: 'low',
  163. 2.0: 'medium',
  164. 3.0: 'high'
  165. }
  166. await send_instructions(
  167. device_id,
  168. regulated_fcu.running_status,
  169. speed_value_dict.get(regulated_fcu.air_valve_speed),
  170. regulated_fcu.running_status
  171. )
  172. logger.info(f'{device_id} - {speed_value_dict.get(regulated_fcu.air_valve_speed)}')