basic.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. from datetime import datetime
  2. from typing import Dict, Optional, Tuple
  3. import arrow
  4. import numpy as np
  5. from loguru import logger
  6. from sqlalchemy.orm import Session
  7. from sqlalchemy.exc import SQLAlchemyError
  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.equipment import VRF
  15. from app.schemas.season import Season
  16. from app.schemas.device.status_timestamp import (
  17. BlowyFeedbackTimeCreate,
  18. BlowyFeedbackTimeUpdate,
  19. HighSpeedTimeCreate,
  20. HighSpeedTimeUpdate,
  21. )
  22. from app.utils.math import round_half_up
  23. class VRFController(EquipmentController):
  24. def __init__(self, device: VRF, target: float, realtime: float):
  25. super(VRFController, self).__init__()
  26. self.device = device
  27. self.target = target
  28. self.realtime = realtime
  29. def get_switch_set(self) -> str:
  30. if np.isnan(self.target):
  31. if self.device.running_status:
  32. switch_set = "off"
  33. else:
  34. switch_set = "hold"
  35. else:
  36. if self.device.running_status:
  37. switch_set = "hold"
  38. else:
  39. switch_set = "on"
  40. self.device.equip_switch_set = switch_set
  41. return switch_set
  42. def get_mode_set(self) -> str:
  43. if self.target > self.realtime:
  44. mode_set = "cooling"
  45. else:
  46. mode_set = "cooling"
  47. self.device.mode_set = mode_set
  48. return mode_set
  49. def get_temperature_set(self) -> float:
  50. if self.realtime >= 24.0:
  51. diff = self.device.return_air_temp - self.realtime
  52. if diff > 0:
  53. new_temperature_set = self.target + diff
  54. elif diff <= 0:
  55. new_temperature_set = self.target
  56. else:
  57. if np.isnan(self.device.current_temperature_set):
  58. new_temperature_set = self.realtime
  59. else:
  60. new_temperature_set = self.device.current_temperature_set
  61. elif self.realtime <= 22.0:
  62. new_temperature_set = 28.0
  63. else:
  64. if np.isnan(self.device.current_temperature_set):
  65. new_temperature_set = 25.0
  66. else:
  67. new_temperature_set = self.device.current_temperature_set
  68. new_temperature_set = round_half_up(new_temperature_set)
  69. self.device.temperature_set = new_temperature_set
  70. return self.device.temperature_set
  71. def get_speed_set(self) -> str:
  72. if self.realtime >= 24.0:
  73. speed = "M"
  74. elif self.realtime <= 22.0:
  75. speed = "LL"
  76. elif self.device.return_air_temp - self.target >= 3.0:
  77. speed = "HH"
  78. elif self.device.return_air_temp - self.target >= 1.0:
  79. speed = "M"
  80. else:
  81. if self.device.speed == "missing":
  82. speed = "M"
  83. else:
  84. speed = self.device.speed
  85. self.device.speed_set = speed
  86. return speed
  87. async def run(self):
  88. if self.get_switch_set() == "on":
  89. self.get_speed_set()
  90. self.get_mode_set()
  91. self.get_temperature_set()
  92. if self.device.temperature_set == self.device.current_temperature_set:
  93. self.device.temperature_set = 0.0
  94. if self.device.speed_set == self.device.speed:
  95. self.device.speed_set = "hold"
  96. self.device.equip_switch_set = "hold"
  97. if not self.device.running_status:
  98. self.device.equip_switch_set = "on"
  99. self.get_speed_set()
  100. else:
  101. if not self.device.running_status:
  102. self.device.equip_switch_set = "hold"
  103. def get_results(self):
  104. return self.device
  105. class VRFController1(VRFController):
  106. def __init__(
  107. self,
  108. device: VRF,
  109. target: float,
  110. realtime: float,
  111. feedback: FeedbackValue,
  112. blow_feedback_time: datetime,
  113. high_speed_time: datetime,
  114. season: Season,
  115. ):
  116. super(VRFController1, self).__init__(device, target, realtime)
  117. self.feedback = feedback
  118. self.blow_feedback_time = blow_feedback_time
  119. self.high_speed_time = high_speed_time
  120. self.season = season
  121. def get_temperature_set(self) -> float:
  122. if self.season == Season.transition:
  123. new_temperature_set = np.NAN
  124. elif self.season == Season.cooling:
  125. new_temperature_set = np.NAN
  126. if np.isnan(self.target):
  127. return new_temperature_set
  128. # Default temperature set.
  129. if not self.device.running_status:
  130. new_temperature_set = self.target
  131. # lower limit.
  132. if self.realtime <= 22.0:
  133. new_temperature_set = 28.0
  134. # recovery
  135. # if self.device.temperature_set == 28.0 and self.realtime > 25.0:
  136. # new_temperature_set = self.target
  137. # feedback
  138. if self.feedback.value != "null" and self.device.current_temperature_set:
  139. if (
  140. self.feedback == FeedbackValue.a_little_cold
  141. or self.feedback == FeedbackValue.so_cold
  142. ):
  143. if self.feedback == FeedbackValue.so_cold:
  144. if self.device.speed == "LL":
  145. new_temperature_set = (
  146. self.device.current_temperature_set + 2.0
  147. )
  148. else:
  149. new_temperature_set = (
  150. self.device.current_temperature_set + 1.0
  151. )
  152. else:
  153. new_temperature_set = self.device.current_temperature_set + 1.0
  154. elif (
  155. self.feedback == FeedbackValue.a_little_hot
  156. or self.feedback == FeedbackValue.so_hot
  157. ):
  158. if (
  159. self.feedback == FeedbackValue.so_hot
  160. and self.device.speed == "HH"
  161. ):
  162. if self.device.speed == "HH":
  163. new_temperature_set = (
  164. self.device.current_temperature_set - 2.0
  165. )
  166. else:
  167. new_temperature_set = (
  168. self.device.current_temperature_set - 1.0
  169. )
  170. else:
  171. new_temperature_set = self.device.current_temperature_set - 1.0
  172. if not np.isnan(new_temperature_set):
  173. new_temperature_set = max(24.0, min(28.0, new_temperature_set))
  174. else:
  175. new_temperature_set = np.NAN
  176. if np.isnan(self.target):
  177. return new_temperature_set
  178. if not self.device.running_status:
  179. new_temperature_set = self.target
  180. if self.realtime >= 27.0:
  181. new_temperature_set = 18.0
  182. if self.feedback.value != "null" and self.device.current_temperature_set:
  183. if (
  184. self.feedback == FeedbackValue.a_little_cold
  185. or self.feedback == FeedbackValue.so_cold
  186. ):
  187. if self.feedback == FeedbackValue.so_cold:
  188. if self.device.speed == "M":
  189. new_temperature_set = (
  190. self.device.current_temperature_set + 1.0
  191. )
  192. else:
  193. new_temperature_set = (
  194. self.device.current_temperature_set + 2.0
  195. )
  196. else:
  197. new_temperature_set = self.device.current_temperature_set + 1.0
  198. elif (
  199. self.feedback == FeedbackValue.a_little_hot
  200. or self.feedback == FeedbackValue.so_hot
  201. ):
  202. if self.feedback == FeedbackValue.so_hot:
  203. if self.device.speed == "M":
  204. new_temperature_set = (
  205. self.device.current_temperature_set - 1.0
  206. )
  207. else:
  208. new_temperature_set = (
  209. self.device.current_temperature_set - 2.0
  210. )
  211. else:
  212. new_temperature_set = self.device.current_temperature_set - 1.0
  213. if not np.isnan(new_temperature_set):
  214. new_temperature_set = max(18.0, min(24.0, new_temperature_set))
  215. new_temperature_set = round_half_up(new_temperature_set)
  216. self.device.temperature_set = new_temperature_set
  217. return new_temperature_set
  218. def get_speed_set(self) -> str:
  219. if self.season == Season.transition:
  220. if np.isnan(self.target):
  221. new_speed = "hold"
  222. else:
  223. if self.device.running_status:
  224. new_speed = "hold"
  225. if self.feedback == FeedbackValue.noisy_or_blowy:
  226. if self.device.speed == "HH":
  227. new_speed = "M"
  228. elif self.device.speed == "M":
  229. new_speed = "LL"
  230. else:
  231. new_speed = "LL"
  232. else:
  233. new_speed = "M"
  234. elif self.season == Season.cooling:
  235. new_speed = "hold"
  236. if np.isnan(self.target):
  237. return new_speed
  238. # Default speed set:
  239. if not self.device.running_status:
  240. new_speed = "M"
  241. # Lower limit.
  242. if self.realtime <= 22.0:
  243. new_speed = "LL"
  244. # Feedback.
  245. if self.feedback == FeedbackValue.so_cold:
  246. if self.device.return_air_temp and self.device.current_temperature_set:
  247. if (
  248. self.device.return_air_temp
  249. > self.device.current_temperature_set
  250. ):
  251. if self.device.speed == "HH":
  252. new_speed = "M"
  253. elif self.device.speed == "M":
  254. new_speed = "LL"
  255. elif self.feedback == FeedbackValue.so_hot:
  256. if self.device.speed == "LL":
  257. new_speed = "M"
  258. elif self.device.speed == "M":
  259. new_speed = "HH"
  260. elif self.feedback == FeedbackValue.noisy_or_blowy:
  261. if self.device.speed == "HH":
  262. new_speed = "M"
  263. elif self.device.speed == "M":
  264. new_speed = "LL"
  265. else:
  266. new_speed = "LL"
  267. else:
  268. new_speed = "hold"
  269. if np.isnan(self.target):
  270. return new_speed
  271. # Default speed set:
  272. if not self.device.running_status:
  273. new_speed = "M"
  274. # Lower limit.
  275. if self.realtime >= 28.0:
  276. new_speed = "LL"
  277. # Feedback.
  278. if self.feedback == FeedbackValue.so_hot:
  279. if self.device.return_air_temp and self.device.current_temperature_set:
  280. if (
  281. self.device.return_air_temp
  282. < self.device.current_temperature_set
  283. ):
  284. if self.device.speed == "HH":
  285. new_speed = "M"
  286. elif self.device.speed == "M":
  287. new_speed = "LL"
  288. elif self.feedback == FeedbackValue.so_cold:
  289. if self.device.speed == "LL":
  290. new_speed = "M"
  291. elif self.device.speed == "M":
  292. new_speed = "HH"
  293. elif self.feedback == FeedbackValue.noisy_or_blowy:
  294. if self.device.speed == "HH":
  295. new_speed = "M"
  296. elif self.device.speed == "M":
  297. new_speed = "LL"
  298. else:
  299. new_speed = "LL"
  300. a_now = arrow.utcnow()
  301. # Restriction.
  302. if a_now.date() == arrow.get(self.blow_feedback_time).date():
  303. if new_speed == "HH":
  304. new_speed = "M"
  305. # Recovery.
  306. if (a_now - arrow.get(self.high_speed_time)).seconds > 60 * 60:
  307. if new_speed == "HH":
  308. new_speed = "M"
  309. self.device.speed_set = new_speed
  310. return new_speed
  311. async def run(self):
  312. self.get_switch_set()
  313. self.get_speed_set()
  314. self.get_temperature_set()
  315. async def query_status_time(db: Session, device_id: str) -> Tuple[datetime, datetime]:
  316. feedback_time_in_db = blowy_feedback_time.get_time_by_device(db, device_id)
  317. if feedback_time_in_db:
  318. feedback_time = feedback_time_in_db.timestamp
  319. else:
  320. past = arrow.utcnow().shift(hours=-24)
  321. feedback_time = past.naive
  322. if not device.get(db, device_id):
  323. device.create(db=db, obj_in=DeviceCreate(id=device_id))
  324. blowy_feedback_time.create(
  325. db=db,
  326. obj_in=BlowyFeedbackTimeCreate(
  327. timestamp=feedback_time, device_id=device_id
  328. ),
  329. )
  330. high_speed_time_in_db = high_speed_time.get_time_by_device(db, device_id)
  331. if high_speed_time_in_db:
  332. high_time = high_speed_time_in_db.timestamp
  333. else:
  334. past = arrow.utcnow().shift(hours=-24)
  335. high_time = past.naive
  336. if not device.get(db, device_id):
  337. device.create(db=db, obj_in=DeviceCreate(id=device_id))
  338. high_speed_time.create(
  339. db=db, obj_in=HighSpeedTimeCreate(timestamp=high_time, device_id=device_id)
  340. )
  341. return feedback_time, high_time
  342. @logger.catch()
  343. async def build_acatvi_instructions(
  344. params: ACATVIInstructionsRequest, db: Session
  345. ) -> Dict:
  346. target = (
  347. np.NAN
  348. if params.space_temperature_target == -1.0
  349. else params.space_temperature_target
  350. )
  351. realtime = (
  352. np.NAN
  353. if params.space_realtime_temperature == -1.0
  354. else params.space_realtime_temperature
  355. )
  356. return_air_temp = (
  357. np.NAN
  358. if params.return_air_temperature == -1.0
  359. else params.return_air_temperature
  360. )
  361. current_temperature_set = (
  362. np.NAN
  363. if params.current_temperature_set == -1.0
  364. else round_half_up(params.current_temperature_set)
  365. )
  366. vrf = VRF(
  367. return_air_temp=return_air_temp,
  368. current_temperature_set=current_temperature_set,
  369. speed=params.current_speed,
  370. running_status=params.running_status,
  371. )
  372. act_blowy_feedback_time, act_high_speed_time = await query_status_time(
  373. db, params.device_id
  374. )
  375. controller = VRFController1(
  376. vrf,
  377. target,
  378. realtime,
  379. params.feedback,
  380. act_blowy_feedback_time,
  381. act_high_speed_time,
  382. params.season,
  383. )
  384. await controller.run()
  385. regulated_vrf = controller.get_results()
  386. if params.feedback == FeedbackValue.noisy_or_blowy:
  387. blowy_feedback_time_in_db = blowy_feedback_time.get_time_by_device(
  388. db=db, device_id=params.device_id
  389. )
  390. if blowy_feedback_time_in_db:
  391. blowy_feedback_time.update(
  392. db=db,
  393. db_obj=blowy_feedback_time_in_db,
  394. obj_in=BlowyFeedbackTimeUpdate(
  395. timestamp=datetime.utcnow(), device_id=params.device_id
  396. ),
  397. )
  398. else:
  399. blowy_feedback_time.create(
  400. db=db,
  401. obj_in=BlowyFeedbackTimeCreate(
  402. timestamp=datetime.utcnow(), device_id=params.device_id
  403. ),
  404. )
  405. if regulated_vrf.speed_set == "HH":
  406. high_speed_time_in_db = high_speed_time.get_time_by_device(
  407. db=db, device_id=params.device_id
  408. )
  409. if high_speed_time_in_db:
  410. high_speed_time.update(
  411. db=db,
  412. db_obj=high_speed_time_in_db,
  413. obj_in=HighSpeedTimeUpdate(
  414. timestamp=datetime.utcnow(), device_id=params.device_id
  415. ),
  416. )
  417. else:
  418. high_speed_time.create(
  419. db=db,
  420. obj_in=HighSpeedTimeCreate(
  421. timestamp=datetime.utcnow(), device_id=params.device_id
  422. ),
  423. )
  424. instructions = dict()
  425. instructions.update({"switch_set": regulated_vrf.equip_switch_set})
  426. instructions.update({"speed_set": regulated_vrf.speed_set})
  427. if regulated_vrf.temperature_set and not np.isnan(regulated_vrf.temperature_set):
  428. instructions.update({"temperature_set": regulated_vrf.temperature_set})
  429. return instructions