Procházet zdrojové kódy

update multiple spaces VAV control logic

chenhaiyang před 4 roky
rodič
revize
8b62877605

+ 53 - 7
app/controllers/equipment/vav.py

@@ -1,23 +1,30 @@
 # -*- coding: utf-8 -*-
 
-from typing import Tuple
+from typing import List, Tuple
 
 import numpy as np
 from httpx import AsyncClient
+from fastapi import HTTPException
 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.space import Space
+from app.schemas.sapce_weight import SpaceWeight
 from app.services.platform import DataPlatformService
-from app.services.transfer import Duoduo, SpaceInfoService
+from app.services.transfer import Duoduo, SpaceInfoService, Season
+from app.utils.date import get_time_str
 
 
 class VAVController(EquipmentController):
 
-    def __init__(self, equipment: VAVBox):
+    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'
@@ -29,7 +36,7 @@ class VAVController(EquipmentController):
 
         return strategy
 
-    async def get_temperature(self) -> Tuple[float, float]:
+    async def build_virtual_temperature_v1(self) -> Tuple[float, float]:
         target_list, realtime_list = [], []
         buffer_list = []
         strategy = await self.get_strategy()
@@ -57,8 +64,44 @@ class VAVController(EquipmentController):
 
         return target_result, realtime_result
 
+    async def build_virtual_temperature_v2(self):
+        weights = sorted(self.weights, key=lambda weight: weight.temporary_weight_update_time)
+        if weights[-1].temporary_weight_update_time > get_time_str(60 * 60 * 2, flag='ago'):
+            weight_dic = {weight.id: 0.0 for weight in weights}
+            weight_dic.update({weights[-1].id: weights[-1].temporary_weight})
+        else:
+            weight_dic = {weight.id: weight.default_weight for weight in weights}
+
+        try:
+            virtual_target, virtual_realtime = 0.0, 0.0
+            for sp in self._equipment.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
+
+    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:
-        temperature_set, temperature_realtime = await self.get_temperature()
+        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:
@@ -86,7 +129,7 @@ class VAVController(EquipmentController):
 
 
 @logger.catch()
-async def get_vav_control_result(project_id: str, equipment_id: str) -> VAVBox:
+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)
@@ -102,6 +145,7 @@ async def get_vav_control_result(project_id: str, equipment_id: str) -> VAVBox:
         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:
@@ -137,7 +181,9 @@ async def get_vav_control_result(project_id: str, equipment_id: str) -> VAVBox:
         }
         vav = VAVBox(**temp_vav_params)
 
-    vav_controller = VAVController(vav)
+    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()
 

+ 23 - 2
app/controllers/targets/temperature.py

@@ -6,14 +6,18 @@ from typing import Dict, Tuple
 import arrow
 import numpy as np
 import pandas as pd
+from fastapi import HTTPException
 from httpx import AsyncClient
 from loguru import logger
+from sqlalchemy.orm import Session
 
+from app.crud.space.weight import get_weight_by_space, update_weight
 from app.models.domain.feedback import FeedbackValue
 from app.resources.params import TEMPERATURE_TARGET_WEIGHT
+from app.schemas.sapce_weight import SpaceWeightUpdate
 from app.schemas.target import TemperatureTarget
 from app.services.platform import DataPlatformService
-from app.services.transfer import SpaceInfoService, Season
+from app.services.transfer import SpaceInfoService, Duoduo, Season
 from app.utils.date import get_time_str, TIME_FMT
 
 
@@ -234,6 +238,7 @@ class TemperatureTargetCarrier(Carrier):
     async def fetch_all(self) -> None:
         async with AsyncClient() as client:
             transfer = SpaceInfoService(client, self.project_id, self.object_id)
+            duoduo = Duoduo(client, self.project_id)
             platform = DataPlatformService(client, self.project_id)
 
             realtime_temperature = await platform.get_realtime_temperature(self.object_id)
@@ -241,7 +246,7 @@ class TemperatureTargetCarrier(Carrier):
             current_target = await transfer.get_current_temperature_target()
             is_customized = await transfer.is_customized()
             is_temporary = await transfer.is_temporary()
-            season = await transfer.get_season()
+            season = await duoduo.get_season()
 
         self.result = {
             'realtime_temperature': realtime_temperature,
@@ -326,6 +331,22 @@ class TargetDeliver:
                 await transfer.env_database_set('temperature', controlled_result['new_actual_target'])
 
 
+class WeightFlagDeliver:
+    """
+    Change a space temporary weight when the space receives a feedback about temperature.
+    """
+
+    def __init__(self, db: Session):
+        self.db = db
+
+    def save(self, space: str):
+        weight = get_weight_by_space(self.db, space_id=space)
+        if not weight:
+            logger.error(f'{space} is not in vav_room_weights table')
+        weight_in = SpaceWeightUpdate(temporary_weight=1.0)
+        update_weight(self.db, db_weight=weight, weight_in=weight_in)
+
+
 class TemperatureTargetController:
     """
     Primary flow of temperature target adjustment for Sequoia.