basic.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. # -*- coding: utf-8 -*-
  2. from operator import attrgetter
  3. import arrow
  4. import numpy as np
  5. from app.api.errors.iot import MissingIOTDataError
  6. from app.models.domain.devices import ASHPRequest
  7. from app.schemas.equipment import ASHP
  8. from app.schemas.season import Season
  9. class ASHPGroupController:
  10. def __init__(self, ashp_list: list[ASHP], outdoor_temp_list: list[float], season: Season):
  11. self._season = season
  12. self._ashp_list = ashp_list
  13. self._outdoor_temp_list = outdoor_temp_list
  14. self._interval = 0
  15. self._is_warning = False
  16. def initialization(self) -> tuple[float, float]:
  17. if self._season == Season.cooling:
  18. if self._outdoor_temp_list:
  19. outdoor_temp_avg = np.mean(self._outdoor_temp_list)
  20. else:
  21. outdoor_temp_avg = 22.0
  22. total_device_num = len(self._ashp_list)
  23. if outdoor_temp_avg <= 20.0:
  24. device_num = 1
  25. out_temp_set = 10.0
  26. interval = 45 * 60
  27. elif outdoor_temp_avg < 24.0:
  28. device_num = total_device_num // 3
  29. out_temp_set = 9.0
  30. interval = 0
  31. else:
  32. device_num = total_device_num // 3 + 1
  33. out_temp_set = 8.5
  34. interval = 0
  35. if self._outdoor_temp_list:
  36. if np.max(self._outdoor_temp_list) > 34.0:
  37. device_num += 1
  38. device_num = min(device_num, total_device_num - 1)
  39. elif self._season == Season.heating:
  40. if self._outdoor_temp_list:
  41. outdoor_temp_avg = np.mean(self._outdoor_temp_list)
  42. else:
  43. outdoor_temp_avg = -10.0
  44. total_device_num = len(self._ashp_list)
  45. if outdoor_temp_avg >= 0.0:
  46. device_num = 1
  47. out_temp_set = 45.0
  48. interval = 45 * 60
  49. elif outdoor_temp_avg > -15.0:
  50. device_num = total_device_num // 3
  51. out_temp_set = 45.0
  52. interval = 0
  53. else:
  54. device_num = total_device_num // 3 + 1
  55. out_temp_set = 50.0
  56. interval = 0
  57. if self._outdoor_temp_list:
  58. if np.min(self._outdoor_temp_list) < -15.0:
  59. device_num += 1
  60. device_num = min(device_num, total_device_num - 1)
  61. else:
  62. device_num = 0
  63. interval = 0
  64. out_temp_set = np.NAN
  65. self._interval = interval
  66. return device_num, out_temp_set
  67. def device_num_adjustment(self) -> tuple[float, float]:
  68. out_temp_15_avg = np.mean(
  69. [np.mean(device.out_temp_15min) for device in self._ashp_list if device.running_status])
  70. out_temp_set_15_avg = np.mean(
  71. [np.mean(device.out_temp_set_15min) for device in self._ashp_list if device.running_status])
  72. iplr_15_avg = np.mean([np.mean(device.iplr_15min) for device in self._ashp_list if device.running_status])
  73. out_temp_30_avg = np.mean(
  74. [np.mean(device.out_temp_30min) for device in self._ashp_list if device.running_status])
  75. out_temp_set_30_avg = np.mean(
  76. [np.mean(device.out_temp_set_30min) for device in self._ashp_list if device.running_status])
  77. iplr_30_avg = np.mean([np.mean(device.iplr_30min) for device in self._ashp_list if device.running_status])
  78. device_num_diff = 0
  79. if self._season == Season.cooling:
  80. if out_temp_15_avg < out_temp_set_15_avg - 1 and iplr_15_avg > 0.85:
  81. device_num_diff = 1
  82. if out_temp_30_avg > out_temp_set_30_avg + 1 and iplr_30_avg <= 0.85:
  83. device_num_diff = 1
  84. self._is_warning = True
  85. if out_temp_15_avg >= out_temp_set_15_avg and iplr_15_avg < 0.6:
  86. device_num_diff = -1
  87. elif self._season == Season.heating:
  88. if out_temp_15_avg > out_temp_set_15_avg + 1 and iplr_15_avg > 0.85:
  89. device_num_diff = 1
  90. if out_temp_30_avg > out_temp_set_30_avg + 1 and iplr_30_avg <= 0.85:
  91. device_num_diff = 1
  92. self._is_warning = True
  93. if out_temp_15_avg <= out_temp_set_15_avg + 0.3 and iplr_15_avg < 0.6:
  94. device_num_diff = -1
  95. return device_num_diff, float(out_temp_set_30_avg)
  96. def out_temp_set_adjustment(self) -> float:
  97. out_temp_set = np.NAN
  98. if self._outdoor_temp_list:
  99. current_outdoor_temp = self._outdoor_temp_list[-1]
  100. if self._season == Season.cooling:
  101. if current_outdoor_temp >= 30.0:
  102. out_temp_set = 8.0
  103. elif current_outdoor_temp >= 28.0:
  104. out_temp_set = 8.5
  105. elif current_outdoor_temp >= 26.0:
  106. out_temp_set = 9.0
  107. elif current_outdoor_temp > 22.0:
  108. out_temp_set = 10.0
  109. else:
  110. out_temp_set = 11.0
  111. elif self._season == Season.heating:
  112. if current_outdoor_temp > 10.0:
  113. out_temp_set = 40.0
  114. elif current_outdoor_temp >= 0.0:
  115. out_temp_set = 45.0
  116. elif current_outdoor_temp > -10.0:
  117. out_temp_set = 50.0
  118. else:
  119. out_temp_set = 55.0
  120. return out_temp_set
  121. def build_up(self, diff: float, out_temp_set: float):
  122. if diff > 0:
  123. self._ashp_list = sorted(self._ashp_list, key=attrgetter("acc_run_time"))
  124. for device in self._ashp_list:
  125. if not device.running_status:
  126. device.equip_switch_set = True
  127. diff -= 1
  128. if diff == 0:
  129. break
  130. elif diff < 0:
  131. self._ashp_list = sorted(self._ashp_list, key=attrgetter("acc_run_time"), reverse=True)
  132. for device in self._ashp_list:
  133. if device.running_status:
  134. device.equip_switch_set = False
  135. diff += 1
  136. if diff == 0:
  137. break
  138. if not np.isnan(out_temp_set):
  139. for device in self._ashp_list:
  140. if device.equip_switch_set:
  141. device.out_temp_set = out_temp_set
  142. def turn_off_all(self):
  143. for device in self._ashp_list:
  144. device.equip_switch_set = False
  145. def antifreeze(self):
  146. pass
  147. def run(self):
  148. t = arrow.utcnow()
  149. on_time = arrow.get(self._ashp_list[0].on_time, 'HHmmss')
  150. off_time = arrow.get(self._ashp_list[0].off_time, 'HHmmss')
  151. if off_time.hour > on_time.hour:
  152. if not on_time.hour < t.hour < off_time.hour:
  153. self.turn_off_all()
  154. return
  155. else:
  156. if off_time.hour < t.hour < on_time.hour:
  157. self.turn_off_all()
  158. return
  159. if t.minute < 30:
  160. if t.hour == 1:
  161. if not np.any([device.running_status for device in self._ashp_list]): # no device is running
  162. device_num_diff, out_temp_set = self.initialization()
  163. self.build_up(device_num_diff, out_temp_set)
  164. else:
  165. device_num_diff, out_temp_set = self.device_num_adjustment()
  166. self.build_up(device_num_diff, out_temp_set)
  167. else:
  168. out_temp_set = self.out_temp_set_adjustment()
  169. self.build_up(0, out_temp_set)
  170. def get_results(self) -> tuple[list[ASHP], float, bool]:
  171. return self._ashp_list, self._interval, self._is_warning
  172. async def build_ashp_instructions(params: ASHPRequest) -> dict[str, list | float | bool]:
  173. controller = ASHPGroupController(params.device_list, params.outdoor_temp, params.season)
  174. try:
  175. controller.run()
  176. except (TypeError, IndexError):
  177. raise MissingIOTDataError
  178. device_list, interval, is_warning = controller.get_results()
  179. instructions = {
  180. "device_list": device_list,
  181. "interval": interval,
  182. "is_warning": is_warning
  183. }
  184. return instructions