basic.py 16 KB

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