Browse Source

Merge branch 'dev'
add early start from dev.

highing666 3 years ago
parent
commit
07d45c744c

+ 21 - 5
app/api/routers/devices.py

@@ -1,13 +1,15 @@
-from fastapi import APIRouter, BackgroundTasks, Query
+from fastapi import APIRouter, BackgroundTasks, Depends, Query
+from sqlalchemy.orm import Session
 
+from app.api.dependencies.db import get_db
 from app.controllers.equipment.ahu.supply_air_temperature_set import get_next_supply_air_temperature_set
 from app.controllers.equipment.ahu.thermal_mode import get_thermal_mode
+from app.controllers.equipment.fcu.early_start import get_recommended_early_start_time
 from app.controllers.equipment.fcu.on_ratio import start_on_ratio_mode
-from app.controllers.equipment.pau.switch import get_switch_action
-from app.controllers.equipment.pau.supply_air_temperature_set import get_next_acatfu_supply_air_temperature_set
 from app.controllers.equipment.pau.freq_set import get_next_acatfu_freq_set
-from app.models.domain.devices import DevicesInstructionsBaseResponse
-
+from app.controllers.equipment.pau.supply_air_temperature_set import get_next_acatfu_supply_air_temperature_set
+from app.controllers.equipment.pau.switch import get_switch_action
+from app.models.domain.devices import DevicesInstructionsBaseResponse, DevicesEarlyStartTime
 
 router = APIRouter()
 
@@ -100,3 +102,17 @@ async def get_acatfu_freq_set(
             'fanFreqSet': freq_set
         }
     }
+
+
+@router.get('/early-start/prediction/acatfc', response_model=DevicesEarlyStartTime)
+async def get_acatfc_early_start_time(
+        project_id: str = Query(..., max_length=50, regex='^Pj', alias='projectId'),
+        space_id: str = Query(..., max_length=50, regex='^Sp', alias='spaceId'),
+        db: Session = Depends(get_db)
+):
+    minutes = await get_recommended_early_start_time(db, project_id, space_id)
+    return {
+        'projectId': project_id,
+        'spaceId': space_id,
+        'minutes': minutes
+    }

+ 0 - 0
app/api/routers/model_path/__init__.py


+ 51 - 0
app/api/routers/model_path/early_start.py

@@ -0,0 +1,51 @@
+from typing import List
+
+from fastapi import APIRouter, Depends, HTTPException
+from sqlalchemy.orm import Session
+
+from app.api.dependencies.db import get_db
+from app.crud.model_path.early_start import model_path_early_start_dtr
+from app.schemas.model_path.early_start import (
+    EarlyStartDTRModelPath,
+    EarlyStartDTRModelPathCreate,
+    EarlyStartDTRModelPathUpdate
+)
+
+router = APIRouter()
+
+
+@router.post('/early-start/dtr', response_model=EarlyStartDTRModelPath)
+async def create_model_path(model_path: EarlyStartDTRModelPathCreate, db: Session = Depends(get_db)):
+    return model_path_early_start_dtr.create(db=db, obj_in=model_path)
+
+
+@router.get('/early-start/dtr', response_model=List[EarlyStartDTRModelPath])
+async def read_model_path(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
+    model_paths = model_path_early_start_dtr.get_multi(db, skip=skip, limit=limit)
+    return model_paths
+
+
+@router.get('/early-start/dtr/{device_id}', response_model=EarlyStartDTRModelPath)
+async def read_model_path_by_device(device_id: str, db: Session = Depends(get_db)):
+    db_model_path = model_path_early_start_dtr.get_path_by_device(db=db, device_id=device_id)
+    return db_model_path
+
+
+@router.put('/early-start/dtr/{device_id}', response_model=EarlyStartDTRModelPath)
+async def update_model_path(device_id: str, model_path_in: EarlyStartDTRModelPathUpdate, db: Session = Depends(get_db)):
+    model_path = model_path_early_start_dtr.get_path_by_device(db=db, device_id=device_id)
+    if model_path.device_id == device_id:
+        new_model_path = model_path_early_start_dtr.update(db=db, db_obj=model_path, obj_in=model_path_in)
+    else:
+        raise HTTPException(status_code=404, detail='Model path not found')
+
+    return new_model_path
+
+
+@router.delete('/early-start/dtr/{id}', response_model=EarlyStartDTRModelPath)
+async def delete_model_path(id: int, db: Session = Depends(get_db)):
+    model_path = model_path_early_start_dtr.get(db=db, id=id)
+    if not model_path:
+        raise HTTPException(status_code=404, detail='Model path not found')
+    model_path = model_path_early_start_dtr.remove(db=db, id=id)
+    return model_path

+ 1 - 1
app/api/routers/space.py

@@ -57,7 +57,7 @@ async def read_weight_by_vav(vav_id: str, db: Session = Depends(get_db)):
     return db_weights
 
 
-@router.patch('/weight/{space_id}/{vav_id}', response_model=SpaceWeight)
+@router.put('/weight/{space_id}/{vav_id}', response_model=SpaceWeight)
 async def update_weight_by_space(
         space_id: str,
         vav_id: str,

+ 59 - 0
app/controllers/equipment/fcu/early_start.py

@@ -0,0 +1,59 @@
+from typing import Tuple
+
+from httpx import AsyncClient
+from joblib import load
+from loguru import logger
+from sqlalchemy.orm import Session
+
+from app.core.config import settings
+from app.crud.model_path.early_start import model_path_early_start_dtr
+from app.services.platform import DataPlatformService
+from app.services.transfer import SpaceInfoService
+from app.services.weather import WeatherService
+
+
+class EarlyStartTimeDTRBuilder:
+    """
+    Build early start time by decision tree regression.
+    """
+
+    def __init__(self, model_path: str):
+        self.model_path = f'{settings.ML_MODELS_DIR}{model_path}'
+
+    async def get_prediction(self, indoor_temp: float, outdoor_temp: float) -> float:
+        model = load(self.model_path)
+        pre_time = model.predict([[outdoor_temp, indoor_temp]])
+
+        return pre_time[0]
+
+
+async def fetch_params(project_id: str, space_id: str, db: Session) -> Tuple[float, float, str]:
+    async with AsyncClient() as client:
+        platform = DataPlatformService(client, project_id)
+        space_service = SpaceInfoService(client, project_id, space_id)
+        weather_service = WeatherService(client)
+
+        indoor_temp = await platform.get_realtime_temperature(space_id)
+        weather_info = await weather_service.get_realtime_weather(project_id)
+        outdoor_temp = weather_info.get('temperature')
+
+        device_list = await space_service.get_equipment()
+        device_id = ''
+        for device in device_list:
+            if device.get('category') == 'ACATFC':
+                device_id = device.get('id')
+                break
+
+        model_path = model_path_early_start_dtr.get_path_by_device(db, device_id)
+
+        return indoor_temp, outdoor_temp, model_path.model_path
+
+
+@logger.catch()
+async def get_recommended_early_start_time(db: Session, project_id: str, space_id: str) -> float:
+    indoor_temp, outdoor_temp, model_path = await fetch_params(project_id, space_id, db)
+
+    builder = EarlyStartTimeDTRBuilder(model_path)
+    hour = await builder.get_prediction(indoor_temp, outdoor_temp)
+
+    return hour * 60

+ 2 - 0
app/core/config.py

@@ -25,6 +25,8 @@ class Settings(BaseSettings):
     PROJECT_DIR: DirectoryPath
     LOGS_DIR: DirectoryPath
 
+    ML_MODELS_DIR: DirectoryPath
+
     PROJECT_NAME: str
 
     POSTGRES_SERVER: str

+ 2 - 0
app/crud/__init__.py

@@ -7,3 +7,5 @@
 # from app.schemas.item import ItemCreate, ItemUpdate
 
 # item = CRUDBase[Item, ItemCreate, ItemUpdate](Item)
+
+from .model_path.early_start import model_path_early_start_dtr

+ 0 - 0
app/crud/model_path/__init__.py


+ 16 - 0
app/crud/model_path/early_start.py

@@ -0,0 +1,16 @@
+from sqlalchemy.orm import Session
+
+from app.crud.base import CRUDBase
+from app.models.ml_models_path.early_start import EarlyStartDTRModelPath
+from app.schemas.model_path.early_start import EarlyStartDTRModelPathCreate, EarlyStartDTRModelPathUpdate
+
+
+class CRUDModelPathEarlyStartDTR(
+    CRUDBase[EarlyStartDTRModelPath, EarlyStartDTRModelPathCreate, EarlyStartDTRModelPathUpdate]
+):
+
+    def get_path_by_device(self, db: Session, device_id: str) -> EarlyStartDTRModelPath:
+        return db.query(self.model).filter(EarlyStartDTRModelPath.device_id == device_id).first()
+
+
+model_path_early_start_dtr = CRUDModelPathEarlyStartDTR(EarlyStartDTRModelPath)

+ 2 - 0
app/main.py

@@ -8,6 +8,7 @@ from fastapi import FastAPI
 from loguru import logger
 
 from app.api.routers import targets, equipment, space, item, user, bluetooth, devices, nlp, positioning
+from app.api.routers.model_path import early_start
 from app.core.config import settings
 from app.core.events import create_start_app_handler
 from app.core.logger import InterceptHandler
@@ -26,6 +27,7 @@ def get_application() -> FastAPI:
 
     application.include_router(bluetooth.router, prefix='/bluetooth', tags=['BLE'])
     application.include_router(devices.router, prefix='/devices', tags=['Devices'])
+    application.include_router(early_start.router, prefix='/model-path', tags=['Model Path'])
     application.include_router(equipment.router, prefix='/equip', tags=['Equipment'])
     application.include_router(item.router, prefix='/items', tags=['Items'])
     application.include_router(nlp.router, prefix='/nlp-service', tags=['NLP'])

+ 6 - 0
app/models/domain/devices.py

@@ -14,3 +14,9 @@ class DevicesInstructionsBaseResponse(BaseModel):
     projectId: str
     equipId: str
     output: Dict
+
+
+class DevicesEarlyStartTime(BaseModel):
+    projectId: str
+    spaceId: str
+    minutes: float

+ 0 - 0
app/models/ml_models_path/__init__.py


+ 14 - 0
app/models/ml_models_path/early_start.py

@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from sqlalchemy import Column, Integer, String
+
+from app.db.session import Base
+
+
+class EarlyStartDTRModelPath(Base):
+    __tablename__ = 'early_start_DTR_models'
+
+    id = Column(Integer, primary_key=True, index=True)
+    project_id = Column(String, nullable=False)
+    device_id = Column(String, unique=True, nullable=False)
+    model_path = Column(String)

+ 0 - 0
app/schemas/model_path/__init__.py


+ 32 - 0
app/schemas/model_path/early_start.py

@@ -0,0 +1,32 @@
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+class EarlyStartDTRModelPathBase(BaseModel):
+    project_id: Optional[str] = None
+    device_id: Optional[str] = None
+    model_path: Optional[str] = None
+
+
+class EarlyStartDTRModelPathCreate(EarlyStartDTRModelPathBase):
+    pass
+
+
+class EarlyStartDTRModelPathUpdate(EarlyStartDTRModelPathBase):
+    pass
+
+
+class EarlyStartDTRModelPathInDBBase(EarlyStartDTRModelPathBase):
+    id: Optional[int] = None
+
+    class Config:
+        orm_mode = True
+
+
+class EarlyStartDTRModelPath(EarlyStartDTRModelPathInDBBase):
+    pass
+
+
+class EarlyStartDTRModelPathInDB(EarlyStartDTRModelPathInDBBase):
+    pass

+ 19 - 5
requirements.txt

@@ -1,18 +1,20 @@
 aioredis==1.3.1
 aniso8601==7.0.0
-arrow==0.17.0
+arrow==1.0.3
+astroid==2.5.3
 async-exit-stack==1.0.1
 async-generator==1.10
 async-timeout==3.0.1
 certifi==2020.12.5
 chardet==4.0.0
 click==7.1.2
-Cython==0.29.21
+Cython==0.29.23
 dnspython==2.1.0
 ecdsa==0.16.1
 email-validator==1.1.2
 fastapi==0.63.0
 fastapi-utils==0.2.1
+flake8==3.9.0
 graphql-core==2.3.2
 graphql-relay==2.0.1
 h11==0.12.0
@@ -22,35 +24,47 @@ hpack==4.0.0
 hstspreload==2020.12.22
 httpcore==0.12.2
 httptools==0.1.1
-httpx==0.16.1
+httpx==0.17.1
 hyperframe==6.0.0
 idna==2.10
+isort==5.8.0
+joblib==1.0.1
+lazy-object-proxy==1.6.0
 loguru==0.5.3
 Mako==1.1.3
 MarkupSafe==1.1.1
-numpy==1.19.5
-pandas==1.2.0
+mccabe==0.6.1
+numpy==1.20.2
+pandas==1.2.4
 passlib==1.7.4
 promise==2.3
 psycopg2==2.8.6
 psycopg2-binary==2.8.6
 pyasn1==0.4.8
 pybind11==2.6.1
+pycodestyle==2.7.0
 pydantic==1.7.3
+pyflakes==2.3.1
+pylint==2.7.4
 PyMySQL==1.0.2
 python-dateutil==2.8.1
 python-dotenv==0.15.0
 python-editor==1.0.4
 pytz==2020.5
+regex==2020.11.13
 rfc3986==1.4.0
 Rx==1.6.1
+scikit-learn==0.24.1
 scipy==1.6.0
 six==1.15.0
 sniffio==1.2.0
 SQLAlchemy==1.3.22
 starlette==0.13.6
 tencentcloud-sdk-python==3.0.325
+threadpoolctl==2.1.0
+toml==0.10.2
 ujson==4.0.1
 urllib3==1.26.2
 uvicorn==0.13.3
 websockets==8.1
+wrapt==1.12.1