basic.py 11 KB

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