basic.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. # -*- coding: utf-8 -*-
  2. from typing import Dict, Tuple
  3. import numpy as np
  4. from httpx import AsyncClient
  5. from loguru import logger
  6. from app.controllers.equipment.controller import EquipmentController
  7. from app.models.domain.devices import (
  8. ACATFC2InstructionsRequest,
  9. ACATFC4InstructionsRequest,
  10. )
  11. from app.schemas.equipment import AirValveSpeed, FCU
  12. from app.schemas.instructions import ACATFCInstructions
  13. from app.schemas.space import Space
  14. from app.services.platform import DataPlatformService, InfoCode
  15. from app.services.transfer import SpaceInfoService, Duoduo, Season
  16. from app.utils.math import round_half_up
  17. class FCUController(EquipmentController):
  18. def __init__(self, equipment: FCU, season: Season):
  19. super(FCUController, self).__init__()
  20. self._equipment = equipment
  21. self.season = season
  22. async def get_temperature_target(self) -> float:
  23. target = self._equipment.space.temperature_target
  24. if not np.isnan(target):
  25. setting_target = target
  26. else:
  27. setting_target = 25.0
  28. self._equipment.setting_temperature = setting_target
  29. return setting_target
  30. async def get_mode(self) -> int:
  31. temperature_target = self._equipment.space.temperature_target
  32. if self.season == Season.heating:
  33. mode = 2
  34. if (
  35. temperature_target > 0
  36. and self._equipment.space.realtime_temperature > 0
  37. ):
  38. if temperature_target < self._equipment.space.realtime_temperature:
  39. mode = 1
  40. elif self.season == Season.cooling:
  41. mode = 1
  42. else:
  43. mode = 0
  44. self._equipment.work_mode = mode
  45. return mode
  46. async def get_air_valve_speed(self):
  47. temperature_target = self._equipment.space.temperature_target
  48. if temperature_target > 0:
  49. if self.season == Season.heating:
  50. diff = abs(
  51. self._equipment.space.realtime_temperature - temperature_target
  52. )
  53. if diff >= 1.5:
  54. self._equipment.air_valve_speed = AirValveSpeed.high
  55. elif diff >= 1.0:
  56. self._equipment.air_valve_speed = AirValveSpeed.medium
  57. elif diff >= 0.5:
  58. self._equipment.air_valve_speed = AirValveSpeed.low
  59. else:
  60. self._equipment.air_valve_speed = AirValveSpeed.off
  61. else:
  62. diff = self._equipment.space.realtime_temperature - temperature_target
  63. if diff >= 1.0:
  64. self._equipment.air_valve_speed = AirValveSpeed.high
  65. elif diff >= 0.5:
  66. self._equipment.air_valve_speed = AirValveSpeed.medium
  67. elif diff >= 0.0:
  68. self._equipment.air_valve_speed = AirValveSpeed.low
  69. else:
  70. self._equipment.air_valve_speed = AirValveSpeed.off
  71. else:
  72. self._equipment.air_valve_speed = AirValveSpeed.off
  73. async def get_running_status(self):
  74. if self._equipment.air_valve_speed == AirValveSpeed.off:
  75. self._equipment.running_status = False
  76. else:
  77. self._equipment.running_status = True
  78. async def run(self):
  79. # await self.get_temperature_target()
  80. await self.get_air_valve_speed()
  81. await self.get_running_status()
  82. await self.get_mode()
  83. def get_results(self):
  84. return self._equipment
  85. class FCUControllerV2(EquipmentController):
  86. """Writen by Wenyan"""
  87. def __init__(self, equipment: FCU, season: Season):
  88. super(FCUControllerV2, self).__init__()
  89. self.equipment = equipment
  90. self.season = season
  91. def get_mode(self) -> int:
  92. if self.equipment.water_in_temperature > 0:
  93. if (
  94. self.equipment.water_in_temperature
  95. > self.equipment.space.temperature_target
  96. ):
  97. mode = 2
  98. else:
  99. mode = 1
  100. elif self.equipment.supply_air_temperature > 0:
  101. if (
  102. self.equipment.supply_air_temperature
  103. > self.equipment.space.temperature_target
  104. ):
  105. mode = 2
  106. else:
  107. mode = 1
  108. else:
  109. if self.season == Season.heating:
  110. mode = 2
  111. elif self.season == Season.cooling:
  112. mode = 1
  113. else:
  114. mode = 0
  115. self.equipment.work_mode = mode
  116. return mode
  117. def build_air_valve_speed(self):
  118. target = self.equipment.space.temperature_target
  119. if target > 0:
  120. if self.equipment.space.realtime_temperature > 0:
  121. mode = self.get_mode()
  122. if mode == 2:
  123. diff = target - self.equipment.space.realtime_temperature
  124. elif mode == 1:
  125. diff = self.equipment.space.realtime_temperature - target
  126. else:
  127. diff = 0
  128. if diff <= 0:
  129. speed = AirValveSpeed.off
  130. elif diff < 0.5:
  131. speed = AirValveSpeed.low
  132. elif diff < 1.5:
  133. speed = AirValveSpeed.medium
  134. else:
  135. speed = AirValveSpeed.high
  136. self.equipment.air_valve_speed = speed
  137. else:
  138. self.equipment.air_valve_speed = AirValveSpeed.medium
  139. else:
  140. self.equipment.air_valve_speed = AirValveSpeed.off
  141. def build_running_status(self):
  142. if self.equipment.air_valve_speed == AirValveSpeed.off:
  143. self.equipment.running_status = False
  144. else:
  145. self.equipment.running_status = True
  146. def build_temperature_set(self):
  147. if np.isnan(self.equipment.space.temperature_target):
  148. self.equipment.setting_temperature = 0.0
  149. else:
  150. self.equipment.setting_temperature = self.equipment.space.temperature_target
  151. async def run(self):
  152. self.build_air_valve_speed()
  153. self.build_running_status()
  154. self.get_mode()
  155. self.build_temperature_set()
  156. def get_results(self):
  157. return self.equipment
  158. class ATFC2Controller:
  159. """Two-wipe ATFC logic"""
  160. def __init__(self, space_target: float, space_realtime: float, season: Season):
  161. self.space_target = space_target
  162. self.space_realtime = space_realtime
  163. self.season = season
  164. def build_speed_set(self) -> str:
  165. if self.space_realtime > 0.0 and self.space_target > 0.0:
  166. if self.season == Season.cooling:
  167. diff = self.space_realtime - self.space_target
  168. elif self.season == Season.heating:
  169. diff = self.space_target - self.space_realtime
  170. else:
  171. diff = 0.0
  172. if diff <= 0.0:
  173. speed = "off"
  174. elif diff < 0.5:
  175. speed = "low"
  176. elif diff < 1.5:
  177. speed = "medium"
  178. else:
  179. speed = "high"
  180. else:
  181. speed = "hold"
  182. if self.space_target < 0.0:
  183. speed = "off"
  184. return speed
  185. def build_switch_set(self, new_speed_set: str) -> str:
  186. if new_speed_set == "off":
  187. switch_set = "off"
  188. else:
  189. switch_set = "on"
  190. return switch_set
  191. def build_water_valve_set(self, new_switch_set: str) -> str:
  192. return new_switch_set
  193. def run(self) -> dict:
  194. speed = self.build_speed_set()
  195. switch_set = self.build_switch_set(speed)
  196. water_switch_set = self.build_water_valve_set(switch_set)
  197. res = {
  198. "speed_set": speed,
  199. "switch_set": switch_set,
  200. "water_valve_set": water_switch_set,
  201. }
  202. return res
  203. class ATFC4Controller(ATFC2Controller):
  204. """Four-wipe ATFC logic"""
  205. def __init__(self, space_target: float, space_realtime: float, season: Season):
  206. super().__init__(space_target, space_realtime, season)
  207. def build_speed_set(self) -> str:
  208. speed = super().build_speed_set()
  209. if self.season == Season.heating:
  210. if self.space_target > 0.0 and self.space_realtime > 0.0:
  211. diff = abs(self.space_realtime - self.space_target)
  212. if diff < 0.2:
  213. speed = "off"
  214. elif diff < 0.8:
  215. speed = "low"
  216. elif diff < 1.5:
  217. speed = "medium"
  218. else:
  219. speed = "high"
  220. return speed
  221. def build_water_valve_set(self, new_switch_set: str) -> Tuple[str, str]:
  222. if new_switch_set == "off":
  223. chill_water_valve_set = "off"
  224. hot_water_valve_set = "off"
  225. else:
  226. if self.season == Season.heating:
  227. if self.space_realtime > 0.0:
  228. if self.space_realtime > self.space_target:
  229. chill_water_valve_set = "on"
  230. hot_water_valve_set = "off"
  231. else:
  232. chill_water_valve_set = "off"
  233. hot_water_valve_set = "on"
  234. else:
  235. chill_water_valve_set = "hold"
  236. hot_water_valve_set = "hold"
  237. else:
  238. chill_water_valve_set = "on"
  239. hot_water_valve_set = "off"
  240. return chill_water_valve_set, hot_water_valve_set
  241. def run(self) -> dict:
  242. speed = self.build_speed_set()
  243. switch_set = self.build_switch_set(speed)
  244. chill_water_valve_set, hot_water_valve_set = self.build_water_valve_set(
  245. switch_set
  246. )
  247. res = {
  248. "speed_set": speed,
  249. "switch_set": switch_set,
  250. "chill_water_valve_set": chill_water_valve_set,
  251. "hot_water_valve_set": hot_water_valve_set,
  252. }
  253. return res
  254. @logger.catch()
  255. async def get_fcu_control_result(project_id: str, equipment_id: str) -> Dict:
  256. async with AsyncClient() as client:
  257. duo_duo = Duoduo(client, project_id)
  258. platform = DataPlatformService(client, project_id)
  259. spaces = await duo_duo.get_space_by_equipment(equipment_id)
  260. if project_id == "Pj1101150003":
  261. supply_air_temperature = await platform.get_realtime_data(
  262. InfoCode.supply_temperature, equipment_id
  263. )
  264. water_in_temperature = await platform.get_realtime_data(
  265. InfoCode.water_in_temperature, equipment_id
  266. )
  267. else:
  268. supply_air_temperature = np.NAN
  269. water_in_temperature = np.NAN
  270. for sp in spaces:
  271. transfer = SpaceInfoService(client, project_id, sp.get("id"))
  272. current_target = await transfer.get_current_temperature_target()
  273. realtime_temperature = await platform.get_realtime_temperature(sp.get("id"))
  274. season = await duo_duo.get_season()
  275. temp_space_params = {
  276. "id": sp.get("id"),
  277. "temperature_target": current_target,
  278. "realtime_temperature": realtime_temperature,
  279. }
  280. space = Space(**temp_space_params)
  281. break
  282. fcu_temp_params = {
  283. "id": equipment_id,
  284. "space": space,
  285. "supply_air_temperature": supply_air_temperature,
  286. "water_in_temperature": water_in_temperature,
  287. }
  288. fcu = FCU(**fcu_temp_params)
  289. if project_id == "Pj1101050030":
  290. fcu_controller = FCUController(fcu, season)
  291. else:
  292. fcu_controller = FCUControllerV2(fcu, season)
  293. await fcu_controller.run()
  294. regulated_fcu = fcu_controller.get_results()
  295. output = {
  296. "onOff": 1 if regulated_fcu.running_status else 0,
  297. "mode": regulated_fcu.work_mode,
  298. "speed": regulated_fcu.air_valve_speed.value,
  299. "temperature": float(round_half_up(regulated_fcu.setting_temperature, 1)),
  300. "water": 1 if regulated_fcu.running_status else 0,
  301. }
  302. return output
  303. @logger.catch()
  304. async def build_acatfc2_instructions(
  305. params: ACATFC2InstructionsRequest,
  306. ) -> ACATFCInstructions:
  307. space = Space(
  308. temperature_target=params.space_temperature_target,
  309. realtime_temperature=params.space_realtime_temperature,
  310. )
  311. fcu = FCU(space=space, supply_air_temperature=-1.0, water_in_temperature=-1.0)
  312. controller = FCUControllerV2(fcu, Season(params.season))
  313. await controller.run()
  314. regulated_fcu = controller.get_results()
  315. instructions = ACATFCInstructions(
  316. switch_set=1 if regulated_fcu.running_status else 0,
  317. speed_set=regulated_fcu.air_valve_speed.value,
  318. temperature_set=float(round_half_up(regulated_fcu.setting_temperature, 1)),
  319. mode_set=regulated_fcu.work_mode,
  320. water_valve_switch_set=1 if regulated_fcu.running_status else 0,
  321. )
  322. return instructions
  323. @logger.catch()
  324. async def build_acatfc4_instructions(
  325. params: ACATFC4InstructionsRequest,
  326. ) -> ACATFCInstructions:
  327. space = Space(
  328. temperature_target=params.space_temperature_target,
  329. realtime_temperature=params.space_realtime_temperature,
  330. )
  331. fcu = FCU(space=space)
  332. controller = FCUController(fcu, Season(params.season))
  333. await controller.run()
  334. regulated_fcu = controller.get_results()
  335. instructions = ACATFCInstructions(
  336. switch_set=1 if regulated_fcu.running_status else 0,
  337. speed_set=regulated_fcu.air_valve_speed.value,
  338. temperature_set=float(round_half_up(regulated_fcu.setting_temperature, 1)),
  339. mode_set=regulated_fcu.work_mode,
  340. water_valve_switch_set=1 if regulated_fcu.running_status else 0,
  341. )
  342. return instructions
  343. @logger.catch()
  344. async def build_acatfc2_instructions_v2(params: ACATFC2InstructionsRequest) -> dict:
  345. space_realtime = (
  346. params.space_realtime_temperature if params.space_realtime_temperature else -1
  347. )
  348. controller = ATFC2Controller(
  349. params.space_temperature_target, space_realtime, Season(params.season)
  350. )
  351. instructions = controller.run()
  352. return instructions
  353. @logger.catch()
  354. async def build_acatfc4_instructions_v2(params: ACATFC4InstructionsRequest) -> dict:
  355. space_realtime = (
  356. params.space_realtime_temperature if params.space_realtime_temperature else -1
  357. )
  358. controller = ATFC4Controller(
  359. params.space_temperature_target, space_realtime, Season(params.season)
  360. )
  361. instructions = controller.run()
  362. return instructions