basic.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. from datetime import datetime
  2. from typing import Dict, Tuple
  3. import arrow
  4. import numpy as np
  5. from sqlalchemy.orm import Session
  6. from app.api.errors.iot import MissingIOTDataError
  7. from app.controllers.equipment.controller import EquipmentController
  8. from app.crud.device.device import device
  9. from app.crud.device.status_timestamp import blowy_feedback_time, high_speed_time
  10. from app.models.domain.devices import ACATVIInstructionsRequest
  11. from app.models.domain.feedback import FeedbackValue
  12. from app.schemas.device.device import DeviceCreate
  13. from app.schemas.device.status_timestamp import BlowyFeedbackTimeCreate, HighSpeedTimeCreate
  14. from app.schemas.equipment import VRF, VRFMode
  15. from app.utils.math import round_half_up
  16. class VRFController(EquipmentController):
  17. def __init__(
  18. self,
  19. vrf: VRF,
  20. target: float,
  21. realtime: float,
  22. feedback: FeedbackValue,
  23. on_time: str,
  24. off_time: str,
  25. ):
  26. super(VRFController, self).__init__()
  27. self.device = vrf
  28. self.target = target
  29. self.realtime = realtime
  30. self.feedback = feedback
  31. self.on_time = on_time
  32. self.off_time = off_time
  33. def get_switch_set(self) -> str:
  34. if self.feedback.value == FeedbackValue.switch_off:
  35. if self.device.running_status:
  36. switch_set = "off"
  37. else:
  38. switch_set = "hold"
  39. elif self.feedback.value == FeedbackValue.switch_on:
  40. if not self.device.running_status:
  41. switch_set = "on"
  42. else:
  43. switch_set = "hold"
  44. else:
  45. utc = arrow.utcnow()
  46. now = utc.to("Asia/Shanghai")
  47. ago = now.shift(seconds=-150).format("HHmmSS")
  48. later = now.shift(seconds=150).format("HHmmSS")
  49. if ago <= self.on_time <= later:
  50. if not self.device.running_status:
  51. switch_set = "on"
  52. else:
  53. switch_set = "hold"
  54. elif ago <= self.off_time <= later:
  55. if self.device.running_status:
  56. switch_set = "off"
  57. else:
  58. switch_set = "hold"
  59. else:
  60. switch_set = "hold"
  61. self.device.equip_switch_set = switch_set
  62. return switch_set
  63. def get_temperature_set(self) -> float:
  64. if self.device.work_mode == VRFMode.ventilation:
  65. new_temperature_set = np.NAN
  66. elif self.device.work_mode == VRFMode.cooling:
  67. new_temperature_set = np.NAN
  68. if self.target is None:
  69. return new_temperature_set
  70. # Default temperature set.
  71. if not self.device.running_status:
  72. new_temperature_set = 26.0
  73. # feedback
  74. if self.feedback.value != "null" and self.device.current_temperature_set:
  75. if (
  76. self.feedback == FeedbackValue.a_little_cold
  77. or self.feedback == FeedbackValue.so_cold
  78. ):
  79. if self.feedback == FeedbackValue.so_cold:
  80. if self.device.speed == "LL":
  81. new_temperature_set = self.device.current_temperature_set + 2.0
  82. else:
  83. new_temperature_set = self.device.current_temperature_set + 1.0
  84. else:
  85. new_temperature_set = self.device.current_temperature_set + 1.0
  86. elif self.feedback == FeedbackValue.a_little_hot or self.feedback == FeedbackValue.so_hot:
  87. if self.feedback == FeedbackValue.so_hot and self.device.speed == "HH":
  88. if self.device.speed == "HH":
  89. new_temperature_set = self.device.current_temperature_set - 2.0
  90. else:
  91. new_temperature_set = self.device.current_temperature_set - 1.0
  92. else:
  93. new_temperature_set = self.device.current_temperature_set - 1.0
  94. if not np.isnan(new_temperature_set):
  95. new_temperature_set = max(24.0, min(28.0, new_temperature_set))
  96. elif self.device.work_mode == VRFMode.heating:
  97. new_temperature_set = np.NAN
  98. if self.target is None:
  99. return new_temperature_set
  100. if not self.device.running_status:
  101. new_temperature_set = 24.0
  102. if self.feedback.value != "null" and self.device.current_temperature_set:
  103. if self.feedback == FeedbackValue.a_little_cold or self.feedback == FeedbackValue.so_cold:
  104. if self.feedback == FeedbackValue.so_cold:
  105. if self.device.speed == "M":
  106. new_temperature_set = self.device.current_temperature_set + 1.0
  107. else:
  108. new_temperature_set = self.device.current_temperature_set + 2.0
  109. else:
  110. new_temperature_set = self.device.current_temperature_set + 1.0
  111. elif self.feedback == FeedbackValue.a_little_hot or self.feedback == FeedbackValue.so_hot:
  112. if self.feedback == FeedbackValue.so_hot:
  113. if self.device.speed == "M":
  114. new_temperature_set = self.device.current_temperature_set - 1.0
  115. else:
  116. new_temperature_set = self.device.current_temperature_set - 2.0
  117. else:
  118. new_temperature_set = self.device.current_temperature_set - 1.0
  119. if not np.isnan(new_temperature_set):
  120. new_temperature_set = max(18.0, min(26.0, new_temperature_set))
  121. else:
  122. new_temperature_set = np.NAN
  123. new_temperature_set = round_half_up(new_temperature_set)
  124. self.device.temperature_set = new_temperature_set
  125. return new_temperature_set
  126. def get_speed_set(self) -> str:
  127. if self.device.work_mode == VRFMode.ventilation:
  128. if self.target is None:
  129. new_speed = "hold"
  130. else:
  131. if self.device.running_status:
  132. new_speed = "hold"
  133. if (
  134. self.feedback == FeedbackValue.noisy_or_blowy
  135. or self.feedback == FeedbackValue.a_little_cold
  136. or self.feedback == FeedbackValue.so_cold
  137. ):
  138. if self.device.speed == "HH":
  139. new_speed = "M"
  140. elif self.device.speed == "M":
  141. new_speed = "LL"
  142. else:
  143. new_speed = "hold"
  144. elif self.feedback == FeedbackValue.a_little_hot or self.feedback == FeedbackValue.so_hot:
  145. if self.device.speed == "LL":
  146. new_speed = "M"
  147. elif self.device.speed == "M":
  148. new_speed = "HH"
  149. else:
  150. new_speed = "hold"
  151. else:
  152. new_speed = "M"
  153. elif self.device.work_mode == VRFMode.cooling:
  154. new_speed = "hold"
  155. if self.target is None:
  156. return new_speed
  157. # Default speed set:
  158. if not self.device.running_status:
  159. new_speed = "M"
  160. # Lower limit.
  161. if self.realtime <= 22.0:
  162. new_speed = "LL"
  163. # Feedback.
  164. if self.feedback == FeedbackValue.so_cold:
  165. if self.device.return_air_temp and self.device.current_temperature_set:
  166. if self.device.return_air_temp > self.device.current_temperature_set:
  167. if self.device.speed == "HH":
  168. new_speed = "M"
  169. elif self.device.speed == "M":
  170. new_speed = "LL"
  171. elif self.feedback == FeedbackValue.so_hot:
  172. if self.device.speed == "LL":
  173. new_speed = "M"
  174. elif self.device.speed == "M":
  175. new_speed = "HH"
  176. elif self.feedback == FeedbackValue.noisy_or_blowy:
  177. if self.device.speed == "HH":
  178. new_speed = "M"
  179. elif self.device.speed == "M":
  180. new_speed = "LL"
  181. else:
  182. new_speed = "LL"
  183. elif self.device.work_mode == VRFMode.heating:
  184. new_speed = "hold"
  185. if self.target is None:
  186. return new_speed
  187. # Default speed set:
  188. if not self.device.running_status:
  189. new_speed = "M"
  190. # Lower limit.
  191. if self.realtime >= 28.0:
  192. new_speed = "LL"
  193. # Feedback.
  194. if self.feedback == FeedbackValue.so_hot:
  195. if self.device.return_air_temp and self.device.current_temperature_set:
  196. if self.device.return_air_temp < self.device.current_temperature_set:
  197. if self.device.speed == "HH":
  198. new_speed = "M"
  199. elif self.device.speed == "M":
  200. new_speed = "LL"
  201. elif self.feedback == FeedbackValue.so_cold:
  202. if self.device.speed == "LL":
  203. new_speed = "M"
  204. elif self.device.speed == "M":
  205. new_speed = "HH"
  206. elif self.feedback == FeedbackValue.noisy_or_blowy:
  207. if self.device.speed == "HH":
  208. new_speed = "M"
  209. elif self.device.speed == "M":
  210. new_speed = "LL"
  211. else:
  212. new_speed = "LL"
  213. else:
  214. new_speed = "hold"
  215. self.device.speed_set = new_speed
  216. return new_speed
  217. def ventilation_mode(self) -> str:
  218. new_speed = "hold"
  219. if self.target is None:
  220. return new_speed
  221. else:
  222. if not self.device.running_status:
  223. new_speed = "HH"
  224. else:
  225. if (
  226. self.feedback == FeedbackValue.a_little_cold
  227. or self.feedback == FeedbackValue.so_cold
  228. or self.feedback == FeedbackValue.noisy_or_blowy
  229. ):
  230. if self.device.speed == "HH":
  231. new_speed = "M"
  232. elif self.device.speed == "M":
  233. new_speed = "LL"
  234. if self.feedback == FeedbackValue.a_little_hot or self.feedback == FeedbackValue.so_hot:
  235. if self.device.speed == "LL":
  236. new_speed = "M"
  237. elif self.device.speed == "M":
  238. new_speed = "HH"
  239. self.device.speed_set = new_speed
  240. return new_speed
  241. async def run(self):
  242. try:
  243. self.get_switch_set()
  244. self.get_speed_set()
  245. self.get_temperature_set()
  246. except TypeError:
  247. raise MissingIOTDataError
  248. def get_results(self):
  249. return self.device
  250. async def query_status_time(db: Session, device_id: str) -> Tuple[datetime, datetime]:
  251. feedback_time_in_db = blowy_feedback_time.get_time_by_device(db, device_id)
  252. if feedback_time_in_db:
  253. feedback_time = feedback_time_in_db.timestamp
  254. else:
  255. past = arrow.utcnow().shift(hours=-24)
  256. feedback_time = past.naive
  257. if not device.get(db, device_id):
  258. device.create(db=db, obj_in=DeviceCreate(id=device_id))
  259. blowy_feedback_time.create(
  260. db=db,
  261. obj_in=BlowyFeedbackTimeCreate(
  262. timestamp=feedback_time, device_id=device_id
  263. ),
  264. )
  265. high_speed_time_in_db = high_speed_time.get_time_by_device(db, device_id)
  266. if high_speed_time_in_db:
  267. high_time = high_speed_time_in_db.timestamp
  268. else:
  269. past = arrow.utcnow().shift(hours=-24)
  270. high_time = past.naive
  271. if not device.get(db, device_id):
  272. device.create(db=db, obj_in=DeviceCreate(id=device_id))
  273. high_speed_time.create(db=db, obj_in=HighSpeedTimeCreate(timestamp=high_time, device_id=device_id))
  274. return feedback_time, high_time
  275. async def build_acatvi_instructions(params: ACATVIInstructionsRequest) -> Dict:
  276. vrf = VRF(
  277. return_air_temp=params.return_air_temperature,
  278. current_temperature_set=params.current_temperature_set,
  279. speed=params.current_speed,
  280. running_status=params.running_status,
  281. work_mode=params.work_mode,
  282. )
  283. controller = VRFController(vrf, params.space_temperature_target, params.space_realtime_temperature, params.feedback,
  284. params.on_time, params.off_time)
  285. await controller.run()
  286. regulated_vrf = controller.get_results()
  287. instructions = dict()
  288. instructions.update({"switch_set": regulated_vrf.equip_switch_set})
  289. instructions.update({"speed_set": regulated_vrf.speed_set})
  290. if regulated_vrf.temperature_set and not np.isnan(regulated_vrf.temperature_set):
  291. instructions.update({"temperature_set": regulated_vrf.temperature_set})
  292. return instructions