on_ratio.py 8.5 KB

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