瀏覽代碼

Enpoint mappe Specifico per la modalità filter

master
Lorenzo Pollutri 22 小時之前
父節點
當前提交
a8da8f5359
共有 4 個文件被更改,包括 202 次插入2 次删除
  1. +1
    -0
      app.py
  2. +9
    -0
      config_env.py
  3. +183
    -2
      routes/reslevis.py
  4. +9
    -0
      schemas/reslevis.py

+ 1
- 0
app.py 查看文件

@@ -155,6 +155,7 @@ async def local_then_core(request: Request, call_next):
"/reslevis/updateAlarm",
"/reslevis/updateCoreSettings",
"/reslevis/updateUserPreferences",
"/reslevis/uploadFloorMap",
}
# only proxy CRUD for Reslevis (change prefix or methods if needed)
if (


+ 9
- 0
config_env.py 查看文件

@@ -167,3 +167,12 @@ BLE_AI_MAPS_DIR = os.getenv(
"/data/service/ble-ai-localizer/data/maps",
)

RESLEVIS_MAPS_DIR = os.getenv(
"RESLEVIS_MAPS_DIR",
str(Path(__file__).resolve().parent / "assets" / "maps"),
)

RESLEVIS_MAPS_PUBLIC_PATH = os.getenv(
"RESLEVIS_MAPS_PUBLIC_PATH",
"assets/maps",
).strip("/")

+ 183
- 2
routes/reslevis.py 查看文件

@@ -1,11 +1,13 @@
import asyncio
import json
import os
import tempfile
from urllib.parse import urlencode

from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Request, UploadFile
import httpx
import config_env
from typing import List, Optional
from typing import Any, Dict, List, Optional

from schemas.reslevis import (
BuildingItem,
@@ -26,6 +28,7 @@ from schemas.reslevis import (
GuiConfigItem,
UserPreferencesItem,
UserPreferencesUpdateItem,
FloorMapUploadResponseItem,
CoreSettingsItem,
CoreSettingsUpdateItem,
)
@@ -54,6 +57,7 @@ ALERTS_CORE_BASE_URL = "http://localhost:1902"
TRACKS_CORE_BASE_URL = "http://localhost:1902"
SETTINGS_CORE_BASE_URL = "http://127.0.0.1:1902"
CORE_TIMEOUT = 2.0 # secondi
PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"

async def sync_core_get(request: Request) -> None:
if request.method != "GET":
@@ -171,6 +175,130 @@ def _uid_from_claims(claims: dict) -> str:
raise HTTPException(status_code=401, detail="User identity not found in token")
return str(uid)


def _floor_maps_index_path() -> str:
return os.path.join(config_env.RESLEVIS_MAPS_DIR, "maps.json")


def _read_floor_maps_index() -> Dict[str, Any]:
path = _floor_maps_index_path()
if not os.path.isfile(path):
return {"items": [], "count": 0}

try:
with open(path, "r", encoding="utf-8") as fp:
data = json.load(fp)
except (OSError, ValueError):
return {"items": [], "count": 0}

if not isinstance(data, dict):
return {"items": [], "count": 0}

items = data.get("items")
if not isinstance(items, list):
items = []
return {"items": items, "count": len(items)}


def _write_floor_maps_index(index: Dict[str, Any]) -> None:
maps_dir = config_env.RESLEVIS_MAPS_DIR
os.makedirs(maps_dir, exist_ok=True)

index["count"] = len(index.get("items") or [])
payload = json.dumps(index, ensure_ascii=False, indent=2)

temp_name = None
try:
with tempfile.NamedTemporaryFile("w", dir=maps_dir, delete=False, encoding="utf-8") as tmp:
tmp.write(payload)
tmp.flush()
os.fsync(tmp.fileno())
temp_name = tmp.name

os.replace(temp_name, _floor_maps_index_path())
try:
os.chmod(_floor_maps_index_path(), 0o664)
except OSError:
pass
except OSError as exc:
if temp_name and os.path.exists(temp_name):
try:
os.remove(temp_name)
except OSError:
pass
raise HTTPException(status_code=500, detail="Unable to update maps.json") from exc


def _same_floor(row: Dict[str, Any], floor: int) -> bool:
try:
return int(row.get("floor")) == floor
except (TypeError, ValueError):
return False


def _floor_sort_key(row: Dict[str, Any]) -> int:
try:
return int(row.get("floor"))
except (TypeError, ValueError):
return 0


def _upsert_floor_map_record(floor: int, name: str, metadata: Dict[str, Any]) -> None:
index = _read_floor_maps_index()
item = {
"floor": floor,
"name": name,
"mime_type": "image/png",
"metadata": metadata,
}

items = [r for r in index["items"] if isinstance(r, dict) and not _same_floor(r, floor)]
items.append(item)
items.sort(key=_floor_sort_key)
index["items"] = items
_write_floor_maps_index(index)


def _public_map_path(name: str) -> str:
public_path = config_env.RESLEVIS_MAPS_PUBLIC_PATH
return f"{public_path}/{name}" if public_path else name


def _validate_map_target(maps_dir: str, name: str) -> str:
maps_dir_abs = os.path.abspath(maps_dir)
target_path = os.path.abspath(os.path.join(maps_dir_abs, name))
if os.path.commonpath([maps_dir_abs, target_path]) != maps_dir_abs:
raise HTTPException(status_code=400, detail="Invalid map filename")
return target_path


async def _write_upload_file(upload: UploadFile, target_path: str) -> None:
maps_dir = os.path.dirname(target_path)
temp_name = None
try:
with tempfile.NamedTemporaryFile("wb", dir=maps_dir, delete=False) as tmp:
temp_name = tmp.name
while True:
chunk = await upload.read(1024 * 1024)
if not chunk:
break
tmp.write(chunk)
tmp.flush()
os.fsync(tmp.fileno())

os.replace(temp_name, target_path)
try:
os.chmod(target_path, 0o664)
except OSError:
pass
except OSError as exc:
if temp_name and os.path.exists(temp_name):
try:
os.remove(temp_name)
except OSError:
pass
raise HTTPException(status_code=500, detail="Unable to save floor map") from exc

CORE_GET_SYNC = {
"/reslevis/getGateways": (gateway_repo, _normalize_gateway),
"/reslevis/getZones": (zone_repo, _normalize_zone),
@@ -645,6 +773,59 @@ def updateUserPreferences(
return user_preferences_repo.update(uid, item)


@router.post(
"/uploadFloorMap",
response_model=FloorMapUploadResponseItem,
tags=["Reslevis"],
dependencies=[Depends(get_current_user)],
)
async def uploadFloorMap(
floor: int = Form(...),
file: UploadFile = File(...),
pixel_ratio: float = Form(1),
calibrated: bool = Form(False),
origin_x: int = Form(0),
origin_y: int = Form(0),
grid_size: int = Form(50),
):
if pixel_ratio <= 0:
raise HTTPException(status_code=400, detail="pixel_ratio must be greater than 0")
if grid_size <= 0:
raise HTTPException(status_code=400, detail="grid_size must be greater than 0")

content_type = (file.content_type or "").split(";")[0].strip().lower()
if content_type and content_type != "image/png":
raise HTTPException(status_code=400, detail="Only PNG files are allowed")

signature = await file.read(len(PNG_SIGNATURE))
if signature != PNG_SIGNATURE:
raise HTTPException(status_code=400, detail="Only PNG files are allowed")
await file.seek(0)

maps_dir = config_env.RESLEVIS_MAPS_DIR
os.makedirs(maps_dir, exist_ok=True)

name = f"floor_{floor}.png"
target_path = _validate_map_target(maps_dir, name)
await _write_upload_file(file, target_path)

metadata = {
"pixel_ratio": pixel_ratio,
"calibrated": calibrated,
"origin": [origin_x, origin_y],
"grid_size": grid_size,
}
_upsert_floor_map_record(floor, name, metadata)

return {
"floor": floor,
"name": name,
"path": _public_map_path(name),
"mime_type": "image/png",
"metadata": metadata,
}


@router.post("/postGuiConfig", tags=["Reslevis"], dependencies=[Depends(get_current_user)])
def postGuiConfig(item: GuiConfigItem):
gui_config_repo.add(item)


+ 9
- 0
schemas/reslevis.py 查看文件

@@ -266,6 +266,15 @@ class CalibrationMetadata(BaseModel):
origin: Tuple[int, int]
grid_size: int


class FloorMapUploadResponseItem(BaseModel):
floor: int
name: str
path: str
mime_type: str
metadata: CalibrationMetadata


#??? Da verificate ???
class DownloadFileImmage():
name : str


Loading…
取消
儲存