You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
root 6bd9d920e4 Integrazione mappa per supporto dispositivi mobili 1 月之前
app Integrazione mappa per supporto dispositivi mobili 1 月之前
config Integrazione mappa per supporto dispositivi mobili 1 月之前
data Integrazione mappa per supporto dispositivi mobili 1 月之前
tmp Integrazione mappa per supporto dispositivi mobili 1 月之前
Dockerfile Integrazione mappa per supporto dispositivi mobili 1 月之前
README.md Integrazione mappa per supporto dispositivi mobili 1 月之前
docker-compose.yml Integrazione mappa per supporto dispositivi mobili 1 月之前
entrypoint.sh first commit 1 月之前
requirements.txt Integrazione mappa per supporto dispositivi mobili 1 月之前

README.md

Progetto Container Docker ble-ai-localizer Ambiente: MN reslevis 192.168.1.3 m1.MajorNet-x64.6.6.0-60.bin:03 October 2025 server linux gentoo kernl: 6.6.74-gentoo-x86_64 Python default version 3.10.16

cd /data/service/

########################################################################################### Passo 1 Creazione struttura progetto (sul server) mkdir -p ble-ai-localizer/{app,config,data,models} cd /data/service/ble-ai-localizer

    touch Dockerfile requirements.txt entrypoint.sh docker-compose.yml
    touch app/{__init__.py,main.py,settings.py,auth.py,mqtt_client.py,train.py,infer.py}
    touch config/config.yaml config/secrets.yaml

Cosa hai ottenuto
    app/ codice python (training + inference)
    config/ configurazione (config.yaml versionabile + secrets.yaml non versionabile)
    data/ dataset fingerprint (volume)
    models/ modello salvato (volume)

########################################################################################### Passo 2 .gitignore (consigliato subito) Se usi git: cat > .gitignore « ‘EOF’

secrets e artefatti

config/secrets.yaml data/ models/

python

pycache/ *.pyc .venv/

editor

.vscode/ .idea/ EOF

########################################################################################### Passo 3 Verifica prerequisiti Docker (Gentoo)

Esegui:

docker --version docker compose version

Se docker compose non esiste, di solito su Gentoo:

o hai il plugin docker-cli-compose

o usi docker-compose (legacy). In tal caso dimmelo e ti adatto i comandi.

Quando hai eseguito i comandi del Passo 1–3, nel prossimo step ti faccio creare:

requirements.txt minimale + pinned

Dockerfile compatibile (python:3.10-slim)

docker-compose.yml con volumi (config/, data/, models/) e variabile MODE=train|infer

Se vuoi andare avanti subito senza feedback, incolla qui l’output di:

docker --version docker compose version

docker compose version Docker version 27.4.1, build b9d17eaebb Docker Compose version v2.28.1

Perfetto, sei a posto (Docker 27.4.1 + Compose v2.28.1).

########################################################################################### Passo 4 — Popoliamo i file base (requirements, Dockerfile, entrypoint, compose) 4.1 requirements.txt

Metti dentro (pinned e minimale per il tuo caso):

cat > requirements.txt « ‘EOF’ pandas==2.2.2 numpy==1.26.4 scikit-learn==1.5.1 joblib==1.4.2

paho-mqtt==2.1.0 requests==2.32.3 PyYAML==6.0.2

fastapi==0.115.0 uvicorn[standard]==0.30.6

matplotlib==3.9.2 seaborn==0.13.2 EOF

(Se poi decidiamo che plot non servono in produzione, li togliamo.)

4.2 Dockerfile (Python “fissato”, non-root, robusto) cat > Dockerfile « ‘EOF’ FROM python:3.10-slim

dipendenze native utili a numpy/scikit (safe choice)

RUN apt-get update && apt-get install -y --no-install-recommends
build-essential gcc g++
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt /app/requirements.txt RUN pip install --no-cache-dir -r /app/requirements.txt

COPY app/ /app/app/ COPY entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh

utente non-root

RUN useradd -m appuser USER appuser

cartelle dati/modelli (volumi)

RUN mkdir -p /home/appuser/data /home/appuser/models ENV DATA_DIR=/home/appuser/data ENV MODELS_DIR=/home/appuser/models

ENTRYPOINT ["/app/entrypoint.sh”] EOF

4.3 entrypoint.sh (MODE=train|infer|api) cat > entrypoint.sh « ‘EOF’ #!/usr/bin/env bash set -euo pipefail

MODE="${MODE:-infer}”

case “$MODE” in train) exec python -m app.main train ;; infer) exec python -m app.main infer ;; api) exec uvicorn app.main:api --host 0.0.0.0 --port “${PORT:-8080}” ;; *) echo “MODE non valido: $MODE (usa train|infer|api)” exit 1 ;; esac EOF

Poi rendilo eseguibile:

chmod +x entrypoint.sh

4.4 docker-compose.yml (volumi + config esterna) cat > docker-compose.yml « ‘EOF’ services: ble-ai-localizer: build: . image: ble-ai-localizer:0.1.0 environment: MODE: “infer” CONFIG_FILE: “/app/config/config.yaml” SECRETS_FILE: “/app/config/secrets.yaml” volumes: - ./config:/app/config:ro - ./data:/data - ./models:/models restart: unless-stopped EOF

4.5 config/config.yaml (scheletro) cat > config/config.yaml « ‘EOF’ mqtt: host: “mosquitto” port: 1883 topic: “ble/raw”

api: get_gateways_url: “https://APIHOST:5050/reslevis/getGateways” verify_tls: false refresh_seconds: 300

oidc: token_url: “https://KEYCLOAK/realms/REALM/protocol/openid-connect/token” client_id: “Fastapi” audience: “Fastapi”

paths: dataset: “/data/fingerprint.parquet” model: “/models/model.joblib”

ml:

parametri generali; l’algoritmo è un dettaglio interno

method: “knn” k: 7 weights: “distance” metric: “euclidean” EOF

4.6 config/secrets.yaml (placeholder, non versionare) cat > config/secrets.yaml « ‘EOF’ oidc: client_secret: “CHANGE_ME” username: “CHANGE_ME” password: “CHANGE_ME” EOF

########################################################################################### Passo 5 Metti un main minimale per verificare che il container parte app/main.py cat > app/main.py « ‘EOF’ from fastapi import FastAPI from .settings import load_settings

api = FastAPI()

@api.get("/health”) def health(): return {“status”: “ok”}

def main(): import sys settings = load_settings() print(“Settings loaded. Keys:", list(settings.keys()))

if len(sys.argv) < 2:
    raise SystemExit("Usage: python -m app.main [train|infer]")

cmd = sys.argv[1].lower()
if cmd == "train":
    print("TRAIN mode (placeholder)")
elif cmd == "infer":
    print("INFER mode (placeholder)")
else:
    raise SystemExit("Unknown command")

if name == “main": main() EOF

app/settings.py cat > app/settings.py « ‘EOF’ import os from pathlib import Path import yaml

def _read_yaml(path: str) -> dict: with open(path, “r”, encoding="utf-8”) as f: return yaml.safe_load(f) or {}

def deep_merge(a: dict, b: dict) -> dict: out = dict(a or {}) for k, v in (b or {}).items(): if isinstance(v, dict) and isinstance(out.get(k), dict): out[k] = deep_merge(out[k], v) else: out[k] = v return out

def load_settings() -> dict: cfg_path = os.getenv(“CONFIG_FILE”, “/app/config/config.yaml”) settings = _read_yaml(cfg_path)

secrets_path = os.getenv("SECRETS_FILE", "")
if secrets_path and Path(secrets_path).exists():
    secrets = _read_yaml(secrets_path)
    settings = deep_merge(settings, secrets)

# fallback paths (coerenti con compose)
settings.setdefault("paths", {})
settings["paths"].setdefault("dataset", os.getenv("DATASET_PATH", "/data/fingerprint.parquet"))
settings["paths"].setdefault("model", os.getenv("MODEL_PATH", "/models/model.joblib"))

return settings

EOF

(Gli altri file per ora possono restare vuoti.)

########################################################################################### Passo 6 Build & test smoke test docker compose build

###########################################################################################

END prima creazione container

###########################################################################################

###########################################################################################

  1. Come avviare SOLO ble-ai-localizer (senza impattare gli altri)

    Vai nella directory del progetto: cd /data/service/ble-ai-localizer

    Avvio in background: docker compose -p ble-ai-localizer up -d Perché -p ble-ai-localizer? Imposta esplicitamente il project name, così sei sicuro al 100% di nonagganciare per errore un altro compose.

    Verifica stato (solo di questo progetto): docker compose -p ble-ai-localizer ps

    Log (solo di questo progetto): docker compose -p ble-ai-localizer logs -f

  2. Come stoppare/riavviare SOLO ble-ai-localizer

    Stop (non rimuove container): docker compose -p ble-ai-localizer stop

    Restart: docker compose -p ble-ai-localizer restart

    Stop + rimozione container/network del progetto (NON tocca volumi bind ./data, ./models): docker compose -p ble-ai-localizer down

  3. Aggiornare solo il tuo container (codice o Dockerfile cambiato)

    Ricostruisci l’immagine: docker compose -p ble-ai-localizer build

    Riavvia applicando l’immagine nuova: docker compose -p ble-ai-localizer up -d

    Se vuoi forzare rebuild+restart in un colpo: docker compose -p ble-ai-localizer up -d --build

  4. Esportare l’immagine (backup o deploy su altro server) Esempio export in tar (meglio gzippato): docker save ble-ai-localizer:0.1.0 | gzip > ble-ai-localizer_0.1.0.tar.gz Su altro server: gzip -dc ble-ai-localizer_0.1.0.tar.gz | docker load

#Debug mosquitto_sub -v -h 192.168.1.101 -p 1883 -t ‘#’ -V mqttv311 | grep publish_out

docker compose -p ble-ai-localizer exec -T ble-ai-localizer ls -l /data/config/

#Caso di gateway che non rileva nessun beacon
publish_out/ac233fc1dcd3 [{"timestamp":"2026-01-30T13:40:08.885Z","type":"Gateway","mac":"AC233FC1DCD3","nums":0}]

#Verifica del modello in uso: #Ver 1 docker compose -p ble-ai-localizer exec -T ble-ai-localizer python - «'PY’ import hashlib, joblib, os p=”/data/model/model.joblib” b=open(p,“rb”).read() print(f"FILE: {p}") print(f"sha256={hashlib.sha256(b).hexdigest()[:12]} size={len(b)} bytes”) m=joblib.load(p) print(“TYPE:", type(m)) for k in [“version”,“nan_fill”,“k_floor”,“k_xy”,“weights”,“metric”,“floors”]: print(f”{k}:", getattr(m,k,None)) gws=getattr(m,“feature_gateways”,[]) print(“gateways:", len(gws), “first:", gws[:5]) regs=getattr(m,“xy_regs”,{}) print(“xy_regs floors:", sorted(list(regs.keys()))) PY service “ble-ai-localizer” is not running

#Ver 2 docker compose -p ble-ai-localizer exec -T ble-ai-localizer python - «'PY’ import joblib, pprint m = joblib.load("/data/model/model.joblib”) keys = [ “created_at_utc”,“sklearn_version”,“numpy_version”, “gateways_order”,“nan_fill”,“k_floor”,“k_xy”,“weights”,“metric”,“floors” ] pprint.pprint({k: m.get(k) for k in keys}) PY

#Esempio utilizzo server API: Ottenimento del token: TOKEN=$( curl -k -s -X POST “https://10.251.0.30:10002/realms/API.Server.local/protocol/openid-connect/token
-H “Content-Type: application/x-www-form-urlencoded”
-d “grant_type=password”
-d “client_id=Fastapi”
-d “client_secret=wojuoB7Z5xhlPFrF2lIxJSSdVHCApEgC”
-d “username=core”
-d “password=C0r3_us3r_Cr3d3nt14ls”
-d “audience=Fastapi”
| jq -r ‘.access_token’ )

Utilizzare il token in un API:

curl -k -X ‘GET’
https://10.251.0.30:5050/reslevis/getTrackers
-H ‘accept: application/json’
-H ‘Authorization: Bearer $TOKEN’

#Aggiornamento del software cd /data/service/ble-ai-localizer docker compose up -d --build docker compose -p ble-ai-localizer build docker compose -p ble-ai-localizer up -d --build docker system prune docker rmi ble-ai-localizer:0.1.0

Gestione Sart/Stop Container cd /data/service/ble-ai-localizer docker compose -p ble-ai-localizer up -d docker compose -p ble-ai-localizer logs -f --tail=200 --timestamps docker compose -p ble-ai-localizer stop docker compose -p ble-ai-localizer restart docker compose -p ble-ai-localizer down

Accesso Web MajorNET ResLevis: https://10.251.0.30/frontend/app_reslevis/app.html#home

Accesso Web a Container ble-ai-localizer URL: http://0.0.0.0:8501 http://192.168.1.3:8501/ username e password da file composer: docker-compose.yml UI_USER: “Admin” UI_PASSWORD: “pwdadmin1” <-- facilitate per accesso da mobile

NOTE:

  • Il valore di k utilizzato rappresenta la retta (k=2) o il piano (k>2) tra i beacon di trainig, in addestramento deve coincidere come minimo con il numero di beacon per stanza, ma è un parametro globale per cui va fatto rispettare per tutte le stanze, per cui ad esempio se si decide di catturare 5 misure per stanza k conviene metterlo a 3

  • almeno a 1m dalle pareti della stanza (evitare attaccato al muro su pareti adiacenti)

  • per piani simemtrici le misure di ogni piano conviene farle conincidere

  • aggingere timestamp in fingerprint e registarre traffico mqtt raw (a parita’ di finestra di registrazione potranno essere rivalutati i valori letti)

  • Tempo di tx nel beacon 200 ms

  • potenze 0, -4 ,-8, -12

  • slot di raccolta 30s

  • time 1400

  • Inferenza a 7 sec

  • durante la raccolta occorre almeno un beacon di test per potenza che veiene escluso nell’addestarmento ma usato per l’inferenza con il traffico registrato nella fase di raccolta.

  • testare prima gw e mgtt se regge 200 ms