Pārlūkot izejas kodu

Merge branch 'Tracker_bug_fix_20260429'

master
root pirms 3 dienas
vecāks
revīzija
33477e8ea7
4 mainītis faili ar 174 papildinājumiem un 13 dzēšanām
  1. +4
    -0
      app.py
  2. +124
    -0
      logica_reslevis/tracker_mode.py
  3. +12
    -13
      routes/reslevis.py
  4. +34
    -0
      schemas/reslevis.py

+ 4
- 0
app.py Parādīt failu

@@ -153,7 +153,11 @@ async def stop_mqtt_monitor():
async def local_then_core(request: Request, call_next):
internal_core_proxy_paths = {
"/reslevis/updateAlarm",
<<<<<<< HEAD
"/reslevis/updateCoreSettings",
=======
"/reslevis/updateCoreSettings",
>>>>>>> Tracker_bug_fix_20260429
}
# only proxy CRUD for Reslevis (change prefix or methods if needed)
if (


+ 124
- 0
logica_reslevis/tracker_mode.py Parādīt failu

@@ -0,0 +1,124 @@
import csv
import logging
from typing import Any, Dict, List, Optional

import httpx

log = logging.getLogger(__name__)


def _norm_mac(v: Any) -> str:
if v is None:
return ""
return "".join(ch for ch in str(v).strip().lower() if ch.isalnum())


def _none_if_empty(v: Any) -> Any:
return None if v in ("", None, 0, "0") else v


def _str_or_none(v: Any) -> Optional[str]:
if v in ("", None):
return None
if isinstance(v, (int, float, bool)):
return str(v)
return v


def _normalize_tracker(row: dict) -> dict:
row = dict(row)
row["floor"] = _none_if_empty(row.get("floor"))
row["building"] = _none_if_empty(row.get("building"))
row["battery"] = _str_or_none(row.get("battery"))
row["temperature"] = _str_or_none(row.get("temperature"))
row["acceleration"] = _str_or_none(row.get("acceleration"))
row["heartRate"] = _str_or_none(row.get("heartRate"))
return row


async def _fetch_algorithm(core_base_url: str, timeout: float) -> Optional[str]:
try:
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.get(f"{core_base_url}/reslevis/settings")
if 200 <= resp.status_code < 300:
payload = resp.json()
if isinstance(payload, list) and payload:
value = payload[0].get("current_algorithm")
if value is not None:
return str(value).lower()
except (httpx.RequestError, ValueError):
pass
return None


async def _filter_mode_trackers(
tracker_repo, core_base_url: str, timeout: float
) -> List[Dict[str, Any]]:
try:
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.get(f"{core_base_url}/reslevis/getTrackers")
if 200 <= resp.status_code < 300:
data = resp.json()
if isinstance(data, list):
normalized = [_normalize_tracker(r) for r in data if isinstance(r, dict)]
tracker_repo._write_all(normalized)
return normalized
except (httpx.RequestError, ValueError):
pass
return tracker_repo.list()


def _read_infer_positions(infer_csv_path: str) -> Dict[str, Dict[str, Optional[float]]]:
positions: Dict[str, Dict[str, Optional[float]]] = {}
try:
with open(infer_csv_path, newline="") as f:
reader = csv.DictReader(f, delimiter=";")
for row in reader:
mac = _norm_mac(row.get("mac"))
if not mac:
continue
try:
positions[mac] = {
"x": int(row["x"]) if row.get("x") not in (None, "") else None,
"y": int(row["y"]) if row.get("y") not in (None, "") else None,
}
except (KeyError, ValueError):
continue
except OSError:
log.warning("BLE-AI infer CSV not found: %s", infer_csv_path)
return positions


def _ai_mode_trackers(
tracker_repo, infer_csv_path: str
) -> List[Dict[str, Any]]:
trackers = tracker_repo.list()
positions = _read_infer_positions(infer_csv_path)
if not positions:
return trackers
result = []
for tracker in trackers:
t = dict(tracker)
mac = _norm_mac(t.get("mac"))
if mac and mac in positions:
t["x"] = positions[mac]["x"]
t["y"] = positions[mac]["y"]
result.append(t)
return result


async def get_mode_aware_trackers(
tracker_repo,
core_base_url: str,
infer_csv_path: str,
timeout: float,
) -> List[Dict[str, Any]]:
algorithm = await _fetch_algorithm(core_base_url, timeout)

if algorithm == "filter":
return await _filter_mode_trackers(tracker_repo, core_base_url, timeout)

if algorithm == "ai":
return _ai_mode_trackers(tracker_repo, infer_csv_path)

return tracker_repo.list()

+ 12
- 13
routes/reslevis.py Parādīt failu

@@ -41,6 +41,7 @@ from logica_reslevis.subject import SubjectJsonRepository
from logica_reslevis.alarm import AlarmJsonRepository
from logica_reslevis.track import TrackJsonRepository
from logica_reslevis.tracker_zone import TrackerZoneJsonRepository
from logica_reslevis.tracker_mode import get_mode_aware_trackers

from security import get_current_user

@@ -48,7 +49,11 @@ from security import get_current_user
CORE_BASE_URL = config_env.CORE_API_URL.rstrip("/")
ALERTS_CORE_BASE_URL = "http://localhost:1902"
TRACKS_CORE_BASE_URL = "http://localhost:1902"
<<<<<<< HEAD
SETTINGS_CORE_BASE_URL = "http://localhost:1902"
=======
SETTINGS_CORE_BASE_URL = "http://127.0.0.1:1902"
>>>>>>> Tracker_bug_fix_20260429
CORE_TIMEOUT = 2.0 # secondi

async def sync_core_get(request: Request) -> None:
@@ -126,16 +131,6 @@ def _normalize_gateway(row: dict) -> dict:
row["building"] = _none_if_empty(row.get("building"))
return row

def _normalize_tracker(row: dict) -> dict:
row = dict(row)
row["floor"] = _none_if_empty(row.get("floor"))
row["building"] = _none_if_empty(row.get("building"))
row["battery"] = _str_or_none(row.get("battery"))
row["temperature"] = _str_or_none(row.get("temperature"))
row["acceleration"] = _str_or_none(row.get("acceleration"))
row["heartRate"] = _str_or_none(row.get("heartRate"))
return row

def _normalize_track(row: dict) -> dict:
row = dict(row)
row["ID"] = row.get("ID")
@@ -167,7 +162,6 @@ def _normalize_zone(row: dict) -> dict:
CORE_GET_SYNC = {
"/reslevis/getGateways": (gateway_repo, _normalize_gateway),
"/reslevis/getZones": (zone_repo, _normalize_zone),
"/reslevis/getTrackers": (tracker_repo, _normalize_tracker),
}


@@ -352,8 +346,13 @@ def removeZoneAreaDefinition(zone_area_definition_uuid: str):
tags=["Reslevis"],
dependencies=[Depends(get_current_user)],
)
def getTrackers():
return tracker_repo.list()
async def getTrackers():
return await get_mode_aware_trackers(
tracker_repo,
SETTINGS_CORE_BASE_URL,
config_env.BLE_AI_INFER_CSV,
CORE_TIMEOUT,
)


@router.post("/postTracker", tags=["Reslevis"], dependencies=[Depends(get_current_user)])


+ 34
- 0
schemas/reslevis.py Parādīt failu

@@ -25,7 +25,10 @@ class FloorItem(BaseModel):
class ZoneItem(BaseModel):
id: UUID
name: str
<<<<<<< HEAD
groups: Optional[List[UUID]] = None
=======
>>>>>>> Tracker_bug_fix_20260429
floor: Optional[UUID] = None
building: Optional[UUID] = None

@@ -53,6 +56,7 @@ class GatewayItem(BaseModel):
notes: Optional[str] = None
floor: Optional[UUID] = None
building: Optional[UUID] = None
zone: Optional[UUID] = None

class TrackerItem(BaseModel):
id: UUID
@@ -83,6 +87,29 @@ class SettingItem(BaseModel):
language: str


class CoreSettingsItem(BaseModel):
ID: int
current_algorithm: str
location_confidence: int
last_seen_threshold: int
beacon_metric_size: int
HA_send_interval: int
HA_send_changes_only: bool
RSSI_enforce_threshold: bool
RSSI_min_threshold: int


class CoreSettingsUpdateItem(BaseModel):
current_algorithm: str
last_seen_threshold: int
beacon_metric_size: int
HA_send_interval: int
HA_send_changes_only: bool
RSSI_enforce_threshold: bool
RSSI_min_threshold: int
location_confidence: Optional[int] = None


class GuiConfigItem(BaseModel):
id: str
name: Optional[str] = None
@@ -132,6 +159,8 @@ class AlarmCoreItem(BaseModel):
type: str
status: str
timestamp: str
operator: Optional[UUID] = None
resolution_timestamp: Optional[str] = None


class AlarmStatusUpdateItem(BaseModel):
@@ -178,7 +207,12 @@ class TrackHistoryItem(BaseModel):

class TrackerZoneItem(BaseModel):
id: UUID
<<<<<<< HEAD
name: str
=======
name: Optional[str] = None
zoneList: List[UUID]
>>>>>>> Tracker_bug_fix_20260429
tracker: UUID
zoneList: list[UUID]
days: Optional[str] = None


Notiek ielāde…
Atcelt
Saglabāt