Jelajahi Sumber

Merge branch 'dev'
add early start from dev.

highing666 3 tahun lalu
induk
melakukan
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.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.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.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.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()
 router = APIRouter()
 
 
@@ -100,3 +102,17 @@ async def get_acatfu_freq_set(
             'fanFreqSet': 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
     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(
 async def update_weight_by_space(
         space_id: str,
         space_id: str,
         vav_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
     PROJECT_DIR: DirectoryPath
     LOGS_DIR: DirectoryPath
     LOGS_DIR: DirectoryPath
 
 
+    ML_MODELS_DIR: DirectoryPath
+
     PROJECT_NAME: str
     PROJECT_NAME: str
 
 
     POSTGRES_SERVER: str
     POSTGRES_SERVER: str

+ 2 - 0
app/crud/__init__.py

@@ -7,3 +7,5 @@
 # from app.schemas.item import ItemCreate, ItemUpdate
 # from app.schemas.item import ItemCreate, ItemUpdate
 
 
 # item = CRUDBase[Item, ItemCreate, ItemUpdate](Item)
 # 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 loguru import logger
 
 
 from app.api.routers import targets, equipment, space, item, user, bluetooth, devices, nlp, positioning
 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.config import settings
 from app.core.events import create_start_app_handler
 from app.core.events import create_start_app_handler
 from app.core.logger import InterceptHandler
 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(bluetooth.router, prefix='/bluetooth', tags=['BLE'])
     application.include_router(devices.router, prefix='/devices', tags=['Devices'])
     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(equipment.router, prefix='/equip', tags=['Equipment'])
     application.include_router(item.router, prefix='/items', tags=['Items'])
     application.include_router(item.router, prefix='/items', tags=['Items'])
     application.include_router(nlp.router, prefix='/nlp-service', tags=['NLP'])
     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
     projectId: str
     equipId: str
     equipId: str
     output: Dict
     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
 aioredis==1.3.1
 aniso8601==7.0.0
 aniso8601==7.0.0
-arrow==0.17.0
+arrow==1.0.3
+astroid==2.5.3
 async-exit-stack==1.0.1
 async-exit-stack==1.0.1
 async-generator==1.10
 async-generator==1.10
 async-timeout==3.0.1
 async-timeout==3.0.1
 certifi==2020.12.5
 certifi==2020.12.5
 chardet==4.0.0
 chardet==4.0.0
 click==7.1.2
 click==7.1.2
-Cython==0.29.21
+Cython==0.29.23
 dnspython==2.1.0
 dnspython==2.1.0
 ecdsa==0.16.1
 ecdsa==0.16.1
 email-validator==1.1.2
 email-validator==1.1.2
 fastapi==0.63.0
 fastapi==0.63.0
 fastapi-utils==0.2.1
 fastapi-utils==0.2.1
+flake8==3.9.0
 graphql-core==2.3.2
 graphql-core==2.3.2
 graphql-relay==2.0.1
 graphql-relay==2.0.1
 h11==0.12.0
 h11==0.12.0
@@ -22,35 +24,47 @@ hpack==4.0.0
 hstspreload==2020.12.22
 hstspreload==2020.12.22
 httpcore==0.12.2
 httpcore==0.12.2
 httptools==0.1.1
 httptools==0.1.1
-httpx==0.16.1
+httpx==0.17.1
 hyperframe==6.0.0
 hyperframe==6.0.0
 idna==2.10
 idna==2.10
+isort==5.8.0
+joblib==1.0.1
+lazy-object-proxy==1.6.0
 loguru==0.5.3
 loguru==0.5.3
 Mako==1.1.3
 Mako==1.1.3
 MarkupSafe==1.1.1
 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
 passlib==1.7.4
 promise==2.3
 promise==2.3
 psycopg2==2.8.6
 psycopg2==2.8.6
 psycopg2-binary==2.8.6
 psycopg2-binary==2.8.6
 pyasn1==0.4.8
 pyasn1==0.4.8
 pybind11==2.6.1
 pybind11==2.6.1
+pycodestyle==2.7.0
 pydantic==1.7.3
 pydantic==1.7.3
+pyflakes==2.3.1
+pylint==2.7.4
 PyMySQL==1.0.2
 PyMySQL==1.0.2
 python-dateutil==2.8.1
 python-dateutil==2.8.1
 python-dotenv==0.15.0
 python-dotenv==0.15.0
 python-editor==1.0.4
 python-editor==1.0.4
 pytz==2020.5
 pytz==2020.5
+regex==2020.11.13
 rfc3986==1.4.0
 rfc3986==1.4.0
 Rx==1.6.1
 Rx==1.6.1
+scikit-learn==0.24.1
 scipy==1.6.0
 scipy==1.6.0
 six==1.15.0
 six==1.15.0
 sniffio==1.2.0
 sniffio==1.2.0
 SQLAlchemy==1.3.22
 SQLAlchemy==1.3.22
 starlette==0.13.6
 starlette==0.13.6
 tencentcloud-sdk-python==3.0.325
 tencentcloud-sdk-python==3.0.325
+threadpoolctl==2.1.0
+toml==0.10.2
 ujson==4.0.1
 ujson==4.0.1
 urllib3==1.26.2
 urllib3==1.26.2
 uvicorn==0.13.3
 uvicorn==0.13.3
 websockets==8.1
 websockets==8.1
+wrapt==1.12.1