basic.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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. on_time: str,
  113. off_time: str,
  114. ):
  115. super(VRFController1, self).__init__(device, target, realtime)
  116. self.feedback = feedback
  117. self.on_time = on_time
  118. self.off_time = off_time
  119. def get_switch_set(self) -> str:
  120. if self.feedback.value == FeedbackValue.switch_off:
  121. if self.device.running_status:
  122. switch_set = "off"
  123. else:
  124. switch_set = "hold"
  125. elif self.feedback.value == FeedbackValue.switch_on:
  126. if not self.device.running_status:
  127. switch_set = "on"
  128. else:
  129. switch_set = "hold"
  130. else:
  131. utc = arrow.utcnow()
  132. now = utc.to("Asia/Shanghai")
  133. ago = now.shift(seconds=-150).format("HHmmSS")
  134. later = now.shift(seconds=150).format("HHmmSS")
  135. if ago <= self.on_time <= later:
  136. if not self.device.running_status:
  137. switch_set = "on"
  138. else:
  139. switch_set = "hold"
  140. elif ago <= self.off_time <= later:
  141. if self.device.running_status:
  142. switch_set = "off"
  143. else:
  144. switch_set = "hold"
  145. else:
  146. switch_set = "hold"
  147. self.device.equip_switch_set = switch_set
  148. return switch_set
  149. def get_temperature_set(self) -> float:
  150. if self.device.work_mode == 4.0 or self.device.work_mode == 8.0:
  151. # if self.season == Season.transition:
  152. new_temperature_set = np.NAN
  153. elif self.device.work_mode == 1.0 or self.device.work_mode == 6.0:
  154. # elif self.season == Season.cooling:
  155. new_temperature_set = np.NAN
  156. if np.isnan(self.target):
  157. return new_temperature_set
  158. # Default temperature set.
  159. if not self.device.running_status:
  160. new_temperature_set = 26.0
  161. # lower limit.
  162. if self.realtime <= 22.0:
  163. new_temperature_set = 28.0
  164. # recovery
  165. # if self.device.temperature_set == 28.0 and self.realtime > 25.0:
  166. # new_temperature_set = self.target
  167. # feedback
  168. if self.feedback.value != "null" and self.device.current_temperature_set:
  169. if (
  170. self.feedback == FeedbackValue.a_little_cold
  171. or self.feedback == FeedbackValue.so_cold
  172. ):
  173. if self.feedback == FeedbackValue.so_cold:
  174. if self.device.speed == "LL":
  175. new_temperature_set = (
  176. self.device.current_temperature_set + 2.0
  177. )
  178. else:
  179. new_temperature_set = (
  180. self.device.current_temperature_set + 1.0
  181. )
  182. else:
  183. new_temperature_set = self.device.current_temperature_set + 1.0
  184. elif (
  185. self.feedback == FeedbackValue.a_little_hot
  186. or self.feedback == FeedbackValue.so_hot
  187. ):
  188. if (
  189. self.feedback == FeedbackValue.so_hot
  190. and self.device.speed == "HH"
  191. ):
  192. if self.device.speed == "HH":
  193. new_temperature_set = (
  194. self.device.current_temperature_set - 2.0
  195. )
  196. else:
  197. new_temperature_set = (
  198. self.device.current_temperature_set - 1.0
  199. )
  200. else:
  201. new_temperature_set = self.device.current_temperature_set - 1.0
  202. if not np.isnan(new_temperature_set):
  203. new_temperature_set = max(24.0, min(28.0, new_temperature_set))
  204. elif self.device.work_mode == 2.0 or self.device.work_mode == 7.0:
  205. new_temperature_set = np.NAN
  206. if np.isnan(self.target):
  207. return new_temperature_set
  208. if not self.device.running_status:
  209. new_temperature_set = 20.0
  210. if self.realtime >= 27.0:
  211. new_temperature_set = 18.0
  212. if self.feedback.value != "null" and self.device.current_temperature_set:
  213. if (
  214. self.feedback == FeedbackValue.a_little_cold
  215. or self.feedback == FeedbackValue.so_cold
  216. ):
  217. if self.feedback == FeedbackValue.so_cold:
  218. if self.device.speed == "M":
  219. new_temperature_set = (
  220. self.device.current_temperature_set + 1.0
  221. )
  222. else:
  223. new_temperature_set = (
  224. self.device.current_temperature_set + 2.0
  225. )
  226. else:
  227. new_temperature_set = self.device.current_temperature_set + 1.0
  228. elif (
  229. self.feedback == FeedbackValue.a_little_hot
  230. or self.feedback == FeedbackValue.so_hot
  231. ):
  232. if self.feedback == FeedbackValue.so_hot:
  233. if self.device.speed == "M":
  234. new_temperature_set = (
  235. self.device.current_temperature_set - 1.0
  236. )
  237. else:
  238. new_temperature_set = (
  239. self.device.current_temperature_set - 2.0
  240. )
  241. else:
  242. new_temperature_set = self.device.current_temperature_set - 1.0
  243. if not np.isnan(new_temperature_set):
  244. new_temperature_set = max(18.0, min(24.0, new_temperature_set))
  245. else:
  246. new_temperature_set = np.NAN
  247. new_temperature_set = round_half_up(new_temperature_set)
  248. self.device.temperature_set = new_temperature_set
  249. return new_temperature_set
  250. def get_speed_set(self) -> str:
  251. if self.device.work_mode == 4.0 or self.device.work_mode == 8.0:
  252. # if self.season == Season.transition:
  253. if np.isnan(self.target):
  254. new_speed = "hold"
  255. else:
  256. if self.device.running_status:
  257. new_speed = "hold"
  258. if (
  259. self.feedback == FeedbackValue.noisy_or_blowy
  260. or self.feedback == FeedbackValue.a_little_cold
  261. or self.feedback == FeedbackValue.so_cold
  262. ):
  263. if self.device.speed == "HH":
  264. new_speed = "M"
  265. elif self.device.speed == "M":
  266. new_speed = "LL"
  267. else:
  268. new_speed = "hold"
  269. elif (
  270. self.feedback == FeedbackValue.a_little_hot
  271. or self.feedback == FeedbackValue.so_hot
  272. ):
  273. if self.device.speed == "LL":
  274. new_speed = "M"
  275. elif self.device.speed == "M":
  276. new_speed = "HH"
  277. else:
  278. new_speed = "hold"
  279. else:
  280. new_speed = "M"
  281. elif self.device.work_mode == 1.0 or self.device.work_mode == 6.0:
  282. # elif self.season == Season.cooling:
  283. new_speed = "hold"
  284. if np.isnan(self.target):
  285. return new_speed
  286. # Default speed set:
  287. if not self.device.running_status:
  288. new_speed = "M"
  289. # Lower limit.
  290. if self.realtime <= 22.0:
  291. new_speed = "LL"
  292. # Feedback.
  293. if self.feedback == FeedbackValue.so_cold:
  294. if self.device.return_air_temp and self.device.current_temperature_set:
  295. if (
  296. self.device.return_air_temp
  297. > self.device.current_temperature_set
  298. ):
  299. if self.device.speed == "HH":
  300. new_speed = "M"
  301. elif self.device.speed == "M":
  302. new_speed = "LL"
  303. elif self.feedback == FeedbackValue.so_hot:
  304. if self.device.speed == "LL":
  305. new_speed = "M"
  306. elif self.device.speed == "M":
  307. new_speed = "HH"
  308. elif self.feedback == FeedbackValue.noisy_or_blowy:
  309. if self.device.speed == "HH":
  310. new_speed = "M"
  311. elif self.device.speed == "M":
  312. new_speed = "LL"
  313. else:
  314. new_speed = "LL"
  315. elif self.device.work_mode == 2.0 or self.device.work_mode == 7.0:
  316. new_speed = "hold"
  317. if np.isnan(self.target):
  318. return new_speed
  319. # Default speed set:
  320. if not self.device.running_status:
  321. new_speed = "M"
  322. # Lower limit.
  323. if self.realtime >= 28.0:
  324. new_speed = "LL"
  325. # Feedback.
  326. if self.feedback == FeedbackValue.so_hot:
  327. if self.device.return_air_temp and self.device.current_temperature_set:
  328. if (
  329. self.device.return_air_temp
  330. < self.device.current_temperature_set
  331. ):
  332. if self.device.speed == "HH":
  333. new_speed = "M"
  334. elif self.device.speed == "M":
  335. new_speed = "LL"
  336. elif self.feedback == FeedbackValue.so_cold:
  337. if self.device.speed == "LL":
  338. new_speed = "M"
  339. elif self.device.speed == "M":
  340. new_speed = "HH"
  341. elif self.feedback == FeedbackValue.noisy_or_blowy:
  342. if self.device.speed == "HH":
  343. new_speed = "M"
  344. elif self.device.speed == "M":
  345. new_speed = "LL"
  346. else:
  347. new_speed = "LL"
  348. else:
  349. new_speed = "hold"
  350. # a_now = arrow.utcnow()
  351. # Restriction.
  352. # if a_now.date() == arrow.get(self.blow_feedback_time).date():
  353. # if new_speed == "HH":
  354. # new_speed = "M"
  355. # Recovery.
  356. # logger.debug(self.high_speed_time)
  357. # if (a_now - arrow.get(self.high_speed_time)).seconds > 60 * 60:
  358. # logger.debug(self.high_speed_time)
  359. # if new_speed == "HH":
  360. # new_speed = "M"
  361. self.device.speed_set = new_speed
  362. return new_speed
  363. def ventilation_mode(self) -> str:
  364. new_speed = "hold"
  365. if np.isnan(self.target):
  366. return new_speed
  367. else:
  368. if not self.device.running_status:
  369. new_speed = "HH"
  370. else:
  371. # if self.realtime > 26.0:
  372. # new_speed = "HH"
  373. # elif self.realtime < 22.0:
  374. # new_speed = "LL"
  375. if (
  376. self.feedback == FeedbackValue.a_little_cold
  377. or self.feedback == FeedbackValue.so_cold
  378. or self.feedback == FeedbackValue.noisy_or_blowy
  379. ):
  380. if self.device.speed == "HH":
  381. new_speed = "M"
  382. elif self.device.speed == "M":
  383. new_speed = "LL"
  384. if (
  385. self.feedback == FeedbackValue.a_little_hot
  386. or self.feedback == FeedbackValue.so_hot
  387. ):
  388. if self.device.speed == "LL":
  389. new_speed = "M"
  390. elif self.device.speed == "M":
  391. new_speed = "HH"
  392. self.device.speed_set = new_speed
  393. return new_speed
  394. async def run(self):
  395. self.get_switch_set()
  396. self.get_speed_set()
  397. self.get_temperature_set()
  398. async def query_status_time(db: Session, device_id: str) -> Tuple[datetime, datetime]:
  399. feedback_time_in_db = blowy_feedback_time.get_time_by_device(db, device_id)
  400. if feedback_time_in_db:
  401. feedback_time = feedback_time_in_db.timestamp
  402. else:
  403. past = arrow.utcnow().shift(hours=-24)
  404. feedback_time = past.naive
  405. if not device.get(db, device_id):
  406. device.create(db=db, obj_in=DeviceCreate(id=device_id))
  407. blowy_feedback_time.create(
  408. db=db,
  409. obj_in=BlowyFeedbackTimeCreate(
  410. timestamp=feedback_time, device_id=device_id
  411. ),
  412. )
  413. high_speed_time_in_db = high_speed_time.get_time_by_device(db, device_id)
  414. if high_speed_time_in_db:
  415. high_time = high_speed_time_in_db.timestamp
  416. else:
  417. past = arrow.utcnow().shift(hours=-24)
  418. high_time = past.naive
  419. if not device.get(db, device_id):
  420. device.create(db=db, obj_in=DeviceCreate(id=device_id))
  421. high_speed_time.create(
  422. db=db, obj_in=HighSpeedTimeCreate(timestamp=high_time, device_id=device_id)
  423. )
  424. return feedback_time, high_time
  425. @logger.catch()
  426. async def build_acatvi_instructions(
  427. params: ACATVIInstructionsRequest, db: Session
  428. ) -> Dict:
  429. target = (
  430. np.NAN
  431. if params.space_temperature_target == -1.0
  432. else params.space_temperature_target
  433. )
  434. realtime = (
  435. np.NAN
  436. if params.space_realtime_temperature == -1.0
  437. else params.space_realtime_temperature
  438. )
  439. return_air_temp = (
  440. np.NAN
  441. if params.return_air_temperature == -1.0
  442. else params.return_air_temperature
  443. )
  444. current_temperature_set = (
  445. np.NAN
  446. if params.current_temperature_set == -1.0
  447. else round_half_up(params.current_temperature_set)
  448. )
  449. vrf = VRF(
  450. return_air_temp=return_air_temp,
  451. current_temperature_set=current_temperature_set,
  452. speed=params.current_speed,
  453. running_status=params.running_status,
  454. work_mode=params.work_mode,
  455. )
  456. controller = VRFController1(
  457. vrf, target, realtime, params.feedback, params.on_time, params.off_time
  458. )
  459. await controller.run()
  460. regulated_vrf = controller.get_results()
  461. # if params.feedback == FeedbackValue.noisy_or_blowy:
  462. # blowy_feedback_time_in_db = blowy_feedback_time.get_time_by_device(
  463. # db=db, device_id=params.device_id
  464. # )
  465. # if blowy_feedback_time_in_db:
  466. # blowy_feedback_time.update(
  467. # db=db,
  468. # db_obj=blowy_feedback_time_in_db,
  469. # obj_in=BlowyFeedbackTimeUpdate(
  470. # timestamp=datetime.utcnow(), device_id=params.device_id
  471. # ),
  472. # )
  473. # else:
  474. # blowy_feedback_time.create(
  475. # db=db,
  476. # obj_in=BlowyFeedbackTimeCreate(
  477. # timestamp=datetime.utcnow(), device_id=params.device_id
  478. # ),
  479. # )
  480. # if regulated_vrf.speed_set == "HH":
  481. # high_speed_time_in_db = high_speed_time.get_time_by_device(
  482. # db=db, device_id=params.device_id
  483. # )
  484. # if high_speed_time_in_db:
  485. # high_speed_time.update(
  486. # db=db,
  487. # db_obj=high_speed_time_in_db,
  488. # obj_in=HighSpeedTimeUpdate(
  489. # timestamp=datetime.utcnow(), device_id=params.device_id
  490. # ),
  491. # )
  492. # else:
  493. # high_speed_time.create(
  494. # db=db,
  495. # obj_in=HighSpeedTimeCreate(
  496. # timestamp=datetime.utcnow(), device_id=params.device_id
  497. # ),
  498. # )
  499. instructions = dict()
  500. instructions.update({"switch_set": regulated_vrf.equip_switch_set})
  501. instructions.update({"speed_set": regulated_vrf.speed_set})
  502. if regulated_vrf.temperature_set and not np.isnan(regulated_vrf.temperature_set):
  503. instructions.update({"temperature_set": regulated_vrf.temperature_set})
  504. return instructions