123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- # -*- coding: utf-8 -*-
- from typing import List, Tuple
- import numpy as np
- from fastapi import HTTPException
- from httpx import AsyncClient
- from loguru import logger
- from sqlalchemy.orm import Session
- from app.controllers.equipment.controller import EquipmentController
- from app.crud.space.weight import get_weights_by_vav
- from app.schemas.equipment import VAVBox, FCU
- from app.schemas.sapce_weight import SpaceWeight
- from app.schemas.space import Space
- from app.services.platform import DataPlatformService
- from app.services.transfer import Duoduo, SpaceInfoService, Season
- from app.utils.date import get_time_str
- class VAVController(EquipmentController):
- def __init__(self, equipment: VAVBox, weights: List[SpaceWeight], season: Season):
- super(VAVController, self).__init__()
- self._equipment = equipment
- self.weights = weights
- self.season = season
- async def get_strategy(self):
- strategy = 'Plan A'
- for space in self._equipment.spaces:
- for eq in space.equipment:
- if isinstance(eq, FCU):
- strategy = 'Plan B'
- break
- return strategy
- async def build_virtual_temperature_v1(self) -> Tuple[float, float]:
- target_list, realtime_list = [], []
- buffer_list = []
- strategy = await self.get_strategy()
- for space in self._equipment.spaces:
- if not np.isnan(space.temperature_target):
- target_list.append(space.temperature_target)
- realtime_list.append(space.realtime_temperature)
- if strategy == 'Plan B':
- for eq in space.equipment:
- if isinstance(eq, FCU):
- buffer = (4 - eq.air_valve_speed) / 4
- buffer_list.append(buffer)
- break
- logger.info(f'target list: {target_list}')
- logger.info(f'realtime list: {realtime_list}')
- logger.info(f'buffer list: {buffer_list}')
- total_target = buffer_list + target_list
- total_realtime = buffer_list + realtime_list
- if total_target and total_realtime:
- target_result = np.array(total_target).sum() / len(target_list)
- realtime_result = np.array(total_realtime).sum() / len(realtime_list)
- self._equipment.setting_temperature = target_result
- else:
- target_result, realtime_result = np.NAN, np.NAN
- return target_result, realtime_result
- async def build_virtual_temperature_v2(self):
- valid_spaces = []
- weights = []
- for sp in self._equipment.spaces:
- if sp.realtime_temperature > 0.0 and sp.temperature_target > 0.0:
- valid_spaces.append(sp)
- for weight in self.weights:
- if weight.space_id == sp.id:
- weights.append(weight)
- if valid_spaces:
- weights = sorted(weights, key=lambda x: x.temporary_weight_update_time)
- if weights[-1].temporary_weight_update_time > get_time_str(60 * 60 * 2, flag='ago'):
- weight_dic = {weight.space_id: 0.0 for weight in weights}
- weight_dic.update({weights[-1].space_id: weights[-1].temporary_weight})
- else:
- weight_dic = {weight.space_id: weight.default_weight for weight in weights}
- total_weight_value = 0.0
- for v in weight_dic.values():
- total_weight_value += v
- if total_weight_value > 0:
- weight_dic = {k: v / total_weight_value for k, v in weight_dic.items()}
- else:
- weight_dic.update({list(weight_dic.keys())[0]: 1.0})
- try:
- virtual_target, virtual_realtime = 0.0, 0.0
- for sp in valid_spaces:
- virtual_target += sp.temperature_target * weight_dic.get(sp.id)
- virtual_realtime += sp.realtime_temperature * weight_dic.get(sp.id)
- except KeyError:
- logger.error(f'{self._equipment.id} has wrong vav-space relation')
- raise HTTPException(status_code=404, detail='This VAV box has wrong eq-sp relation')
- self._equipment.virtual_target_temperature = virtual_target
- self._equipment.virtual_realtime_temperature = virtual_realtime
- else:
- self._equipment.virtual_target_temperature = np.NAN
- self._equipment.virtual_realtime_temperature = np.NAN
- async def rectify(self) -> Tuple[float, float]:
- for sp in self._equipment.spaces:
- if sp.realtime_temperature < min(23.0, sp.temperature_target):
- if self.season == Season.heating:
- self._equipment.virtual_target_temperature = min(23.0, sp.temperature_target) + 0.5
- self._equipment.virtual_realtime_temperature = sp.realtime_temperature
- break
- elif sp.realtime_temperature > max(27.0, sp.temperature_target):
- if self.season == Season.cooling:
- self._equipment.virtual_target_temperature = max(27.0, sp.temperature_target)
- self._equipment.virtual_realtime_temperature = sp.realtime_temperature
- break
- return self._equipment.virtual_target_temperature, self._equipment.virtual_realtime_temperature
- async def get_supply_air_flow_set(self) -> float:
- await self.build_virtual_temperature_v2()
- temperature_set, temperature_realtime = await self.rectify()
- if np.isnan(temperature_set) or np.isnan(temperature_realtime):
- supply_air_flow_set = 0.0
- else:
- temperature_supply = self._equipment.supply_air_temperature
- if np.isnan(temperature_supply):
- temperature_supply = 19.0
- logger.info(f'supply air flow: {self._equipment.supply_air_flow}')
- logger.info(f'supply air temperature: {temperature_supply}')
- logger.info(f'set temperature: {temperature_set}')
- logger.info(f'realtime temperature: {temperature_realtime}')
- supply_air_flow_set = self._equipment.supply_air_flow * ((temperature_supply - temperature_realtime)
- / (temperature_supply - temperature_set))
- supply_air_flow_set = max(self._equipment.supply_air_flow_lower_limit, supply_air_flow_set)
- supply_air_flow_set = min(self._equipment.supply_air_flow_upper_limit, supply_air_flow_set)
- self._equipment.supply_air_flow_set = supply_air_flow_set
- return supply_air_flow_set
- async def run(self):
- await self.get_supply_air_flow_set()
- self._equipment.running_status = True
- def get_results(self):
- return self._equipment
- @logger.catch()
- async def get_vav_control_result(db: Session, project_id: str, equipment_id: str) -> VAVBox:
- async with AsyncClient() as client:
- duo_duo = Duoduo(client, project_id)
- platform = DataPlatformService(client, project_id)
- _AHU_LIST = [
- 'Eq1101050030846e0a94670842109f7c8d8db0d44cf5',
- 'Eq1101050030b6b2f1db3d6944afa71e213e0d45d565'
- ]
- realtime_supply_air_temperature_list = []
- for eq in _AHU_LIST:
- realtime_supply_air_temperature_list.append(await platform.get_realtime_supply_air_temperature(eq))
- realtime_supply_air_temperature = np.array(realtime_supply_air_temperature_list).mean()
- realtime_supply_air_flow = await platform.get_realtime_supply_air_flow(equipment_id)
- lower_limit, upper_limit = await platform.get_air_flow_limit(equipment_id)
- season = await duo_duo.get_season()
- served_spaces = await duo_duo.get_space_by_equipment(equipment_id)
- space_objects = []
- for sp in served_spaces:
- sp_id = sp.get('id')
- transfer = SpaceInfoService(client, project_id, sp_id)
- current_target = await transfer.get_current_temperature_target()
- realtime_temperature = await platform.get_realtime_temperature(sp_id)
- related_equipment = await transfer.get_equipment()
- equipment_objects = []
- for eq in related_equipment:
- if eq.get('category') == 'ACATFC':
- speed = await platform.get_fan_speed(eq.get('id'))
- temp_fcu_params = {'id': eq.get('id'), 'air_valve_speed': speed}
- fcu = FCU(**temp_fcu_params)
- equipment_objects.append(fcu)
- temp_space_params = {
- 'id': sp_id,
- 'realtime_temperature': realtime_temperature,
- 'equipment': equipment_objects,
- 'temperature_target': current_target
- }
- space = Space(**temp_space_params)
- space_objects.append(space)
- temp_vav_params = {
- 'id': equipment_id,
- 'spaces': space_objects,
- 'supply_air_temperature': realtime_supply_air_temperature,
- 'supply_air_flow': realtime_supply_air_flow,
- 'supply_air_flow_lower_limit': lower_limit,
- 'supply_air_flow_upper_limit': upper_limit,
- }
- vav = VAVBox(**temp_vav_params)
- weights = get_weights_by_vav(db, equipment_id)
- vav_controller = VAVController(vav, [SpaceWeight.from_orm(weight) for weight in weights], season)
- await vav_controller.run()
- regulated_vav = vav_controller.get_results()
- return regulated_vav
|