on_ratio.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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.5:
  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 = 'medium'
  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, period_time: float, delta_on: float, delta_off: float, last_mode: str) -> float:
  51. if self.period_num == 0:
  52. if last_mode == 'normal':
  53. ratio = 0.8
  54. elif last_mode == 'off':
  55. ratio = 0.2
  56. else:
  57. if self.return_air > self.target - 0.75:
  58. ratio = 0.2
  59. else:
  60. ratio = 0.8
  61. else:
  62. if delta_on <= 0:
  63. ratio = 0.9
  64. else:
  65. try:
  66. if delta_off > 0:
  67. delta_off = 0.0
  68. ratio = (0.5 * (self.target - self.return_air) / period_time - delta_off) / (delta_on - delta_off)
  69. if ratio > 0.9:
  70. ratio = 0.9
  71. if ratio < 0.1:
  72. ratio = 0.1
  73. if delta_on <= 0:
  74. ratio = 0.5
  75. logger.debug(f'delta target: {0.5 * (self.target - self.return_air)}')
  76. except ZeroDivisionError:
  77. ratio = 0.1
  78. return ratio
  79. async def send_instructions(device_id: str, switch: bool, speed: str, water_valve: bool) -> None:
  80. switch_value = 1.0 if switch else 0.0
  81. water_valve_value = 1.0 if water_valve else 0.0
  82. speed_value_dict = {
  83. 'off': 0.0,
  84. 'low': 1.0,
  85. 'medium': 2.0,
  86. 'high': 3.0
  87. }
  88. speed_value = speed_value_dict.get(speed)
  89. async with AsyncClient() as client:
  90. platform = DataPlatformService(client, 'Pj1101080259')
  91. await platform.set_code_value(device_id, code=InfoCode.equip_switch_set, value=switch_value)
  92. await platform.set_code_value(device_id, code=InfoCode.water_valve_switch_set, value=water_valve_value)
  93. await platform.set_code_value(device_id, code=InfoCode.fan_speed_set, value=speed_value)
  94. await platform.set_code_value(device_id, code=InfoCode.work_mode_set, value=2.0)
  95. # await platform.set_code_value(device_id, code=InfoCode.in_cloud_set, value=1.0)
  96. async def fetch_params(device_id: str, period_time: int, last_ratio: float) -> Tuple[float, float, float, float]:
  97. async with AsyncClient() as client:
  98. middle_time = int(round_half_up(period_time * (1 - last_ratio)))
  99. platform = DataPlatformService(client, 'Pj1101080259')
  100. return_air_c = await platform.get_realtime_data(InfoCode.return_air_temperature, device_id)
  101. return_air_a = await platform.get_past_data(InfoCode.return_air_temperature, device_id, period_time)
  102. return_air_b = await platform.get_past_data(InfoCode.return_air_temperature, device_id, middle_time)
  103. delta_all = (return_air_c - return_air_a) / period_time
  104. if 0 < last_ratio < 1:
  105. delta_on = (return_air_b - return_air_a) / (period_time - middle_time)
  106. delta_off = (return_air_c - return_air_b) / middle_time
  107. elif last_ratio == 0.0:
  108. delta_on = 0.0
  109. delta_off = (return_air_c - return_air_b) / middle_time
  110. else:
  111. delta_on = (return_air_b - return_air_a) / (period_time - middle_time)
  112. delta_off = 0.0
  113. return return_air_c, delta_all, delta_on, delta_off
  114. @logger.catch()
  115. async def start_on_ratio_mode(device_id: str, target: float, period_time: int):
  116. _DAILY_ON_TIME = '060000'
  117. _DAILY_OFF_TIME = '220000'
  118. period_num = 0
  119. last_on_ratio = 1.0
  120. last_mode = 'default'
  121. life_count = 0
  122. while life_count < 2000:
  123. try:
  124. time_str = get_time_str()
  125. if _DAILY_ON_TIME <= arrow.get(time_str, TIME_FMT).time().strftime('%H%M%S') < _DAILY_OFF_TIME:
  126. return_air, delta_all, delta_on, delta_off = await fetch_params(device_id, period_time, last_on_ratio)
  127. controller = OnRatioController(target, return_air, period_num)
  128. mode = controller.select_mode()
  129. speed = controller.select_speed(delta_all)
  130. switch = False if speed == 'off' else True
  131. water_valve = controller.select_water_valve()
  132. if mode == 'on_ratio':
  133. on_ratio = controller.calculate_on_ratio(period_time, delta_on, delta_off, last_mode)
  134. on_range = round_half_up(period_time * on_ratio)
  135. off_range = period_time - on_range
  136. logger.debug(f'life count: {life_count}, {device_id}, on time: {on_range}, off time: {off_range}, '
  137. f'on ratio: {on_ratio}, delta_on: {delta_on * 900}, delta_off: {delta_off * 900}')
  138. await send_instructions(device_id, switch, speed, water_valve)
  139. logger.debug(f'{device_id}, {switch}, {speed}, {water_valve}')
  140. await asyncio.sleep(on_range)
  141. await send_instructions(device_id, switch, speed, False)
  142. logger.debug(f'{device_id}, {switch}, {speed}, off')
  143. await asyncio.sleep(off_range)
  144. period_num += 1
  145. last_on_ratio = on_ratio
  146. last_mode = mode
  147. else:
  148. await send_instructions(device_id, switch, speed, water_valve)
  149. logger.debug(f'life count: {life_count}, {device_id}, {switch}, {speed}, {water_valve}')
  150. await asyncio.sleep(period_time)
  151. period_num = 0
  152. last_on_ratio = 1.0
  153. last_mode = mode
  154. life_count += 1
  155. else:
  156. await send_instructions(device_id, False, 'off', False)
  157. period_num = 0
  158. last_on_ratio = 0.0
  159. last_mode = 'off'
  160. await asyncio.sleep(period_time)
  161. except (KeyError, IndexError, TypeError, HTTPException):
  162. await asyncio.sleep(period_time)
  163. continue
  164. @logger.catch()
  165. async def start_control_group_mode(device_id: str, target: float):
  166. async with AsyncClient() as client:
  167. platform = DataPlatformService(client, 'Pj1101080259')
  168. return_air = await platform.get_realtime_data(InfoCode.return_air_temperature, device_id)
  169. space_params = {
  170. 'id': device_id,
  171. 'temperature_target': target,
  172. 'realtime_temperature': return_air
  173. }
  174. space = Space(**space_params)
  175. fcu_params = {
  176. 'id': device_id,
  177. 'space': space
  178. }
  179. fcu = FCU(**fcu_params)
  180. controller = FCUControllerV2(fcu, Season.heating)
  181. await controller.run()
  182. regulated_fcu = controller.get_results()
  183. speed_value_dict = {
  184. 0.0: 'medium',
  185. 1.0: 'low',
  186. 2.0: 'medium',
  187. 3.0: 'high'
  188. }
  189. await send_instructions(
  190. device_id,
  191. True,
  192. speed_value_dict.get(regulated_fcu.air_valve_speed),
  193. regulated_fcu.running_status
  194. )
  195. logger.info(f'{device_id} - {speed_value_dict.get(regulated_fcu.air_valve_speed)}')