basic.py 14 KB

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