on_ratio.py 8.1 KB

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