瀏覽代碼

Integrazione mappa per supporto dispositivi mobili

master
root 1 月之前
父節點
當前提交
6bd9d920e4
共有 77 個文件被更改,包括 13938 次插入9097 次删除
  1. +13
    -18
      Dockerfile
  2. +1
    -1
      README.md
  3. +0
    -58
      app/leaflet_bridge.js
  4. +209
    -41
      app/map_manager.py
  5. +0
    -145
      app/map_manager.py_v1
  6. +0
    -49
      app/map_manager.py_v2
  7. +83
    -81
      app/web_inference.py
  8. +4
    -1
      app/web_suite.py
  9. +4
    -4
      config/config.yaml
  10. +9
    -9
      data/infer/infer.csv
  11. 二進制
      data/maps.bck/floor_0.png
  12. 二進制
      data/maps.bck/floor_1.png
  13. +1
    -0
      data/maps.bck/meta_0.json
  14. +1
    -0
      data/maps.bck/meta_1.json
  15. 二進制
      data/maps.old/floor_0.png
  16. 二進制
      data/maps.old/floor_1.png
  17. +9
    -0
      data/maps.old/meta_0.json
  18. +1
    -0
      data/maps.old/meta_1.json
  19. 二進制
      data/maps/floor_-1.png
  20. +9
    -0
      data/maps/meta_-1.json
  21. +9
    -1
      data/maps/meta_0.json
  22. +9
    -1
      data/maps/meta_1.json
  23. +0
    -0
      data/train/jobs/done/1_1600_450.csv
  24. +2
    -0
      data/train/jobs/done/BC-21_-1_1050_250.csv
  25. +2
    -0
      data/train/jobs/done/BC-21_0_195_1425.csv
  26. +2
    -0
      data/train/jobs/done/BC-21_0_2114_796.csv
  27. +2
    -0
      data/train/jobs/done/BC-21_0_2115_697.csv
  28. +2
    -0
      data/train/jobs/done/BC-21_0_2568_946.csv
  29. +2
    -0
      data/train/jobs/done/BC-21_0_400_100.csv
  30. +2
    -0
      data/train/jobs/done/BC-21_0_619_122.csv
  31. +2
    -0
      data/train/jobs/done/BC-21_1_1050_500.csv
  32. +2
    -0
      data/train/jobs/done/BC-21_1_950_750.csv
  33. +2
    -0
      data/train/jobs/done/BC-22_-1_1650_1100.csv
  34. +2
    -0
      data/train/jobs/done/BC-22_-1_170_1418.csv
  35. +2
    -0
      data/train/jobs/done/BC-22_0_534_999.csv
  36. +2
    -0
      data/train/jobs/done/BC-23_0_1580_180.csv
  37. +2
    -0
      data/train/jobs/done/BC-23_1_130_1386.csv
  38. +2
    -0
      data/train/jobs/done/BC-23_1_1800_600.csv
  39. +2
    -0
      data/train/jobs/done/BC-24_1_1900_1400.csv
  40. +2
    -0
      data/train/jobs/done/BC-24_1_2200_1000.csv
  41. +2
    -0
      data/train/jobs/done/BC-25_0_562_239.csv
  42. +2
    -0
      data/train/jobs/done/BC-25_1_1450_600.csv
  43. +2
    -0
      data/train/jobs/done/BC-25_1_2400_300.csv
  44. +2
    -0
      data/train/jobs/done/BC-25_1_2500_1300.csv
  45. +2
    -0
      data/train/jobs/done/P_1356_1256.csv
  46. +2
    -0
      data/train/jobs/done/P_2142_616.csv
  47. +2
    -0
      data/train/jobs/done/P_2491_543.csv
  48. +2
    -0
      data/train/samples/-1_1050_250.csv
  49. +2
    -0
      data/train/samples/-1_1650_1100.csv
  50. +2
    -0
      data/train/samples/-1_170_1418.csv
  51. +2
    -0
      data/train/samples/0_1356_1256.csv
  52. +2
    -0
      data/train/samples/0_1580_180.csv
  53. +2
    -0
      data/train/samples/0_1600_450.csv
  54. +2
    -0
      data/train/samples/0_195_1425.csv
  55. +2
    -0
      data/train/samples/0_2114_796.csv
  56. +2
    -0
      data/train/samples/0_2115_697.csv
  57. +2
    -0
      data/train/samples/0_2142_616.csv
  58. +2
    -0
      data/train/samples/0_2491_543.csv
  59. +2
    -0
      data/train/samples/0_2568_946.csv
  60. +2
    -0
      data/train/samples/0_400_100.csv
  61. +2
    -0
      data/train/samples/0_534_999.csv
  62. +2
    -0
      data/train/samples/0_562_239.csv
  63. +2
    -0
      data/train/samples/0_619_122.csv
  64. +2
    -0
      data/train/samples/1_1050_400.csv
  65. +2
    -0
      data/train/samples/1_1050_500.csv
  66. +2
    -0
      data/train/samples/1_130_1386.csv
  67. +2
    -0
      data/train/samples/1_1450_600.csv
  68. +2
    -0
      data/train/samples/1_1800_600.csv
  69. +2
    -0
      data/train/samples/1_1900_1400.csv
  70. +2
    -0
      data/train/samples/1_2200_1000.csv
  71. +2
    -0
      data/train/samples/1_2400_300.csv
  72. +2
    -0
      data/train/samples/1_2500_1300.csv
  73. +2
    -0
      data/train/samples/1_2750_200.csv
  74. +2
    -0
      data/train/samples/1_950_750.csv
  75. +1
    -0
      docker-compose.yml
  76. +12
    -1
      requirements.txt
  77. +13461
    -8687
      tmp/main_process.log

+ 13
- 18
Dockerfile 查看文件

@@ -1,33 +1,28 @@
FROM python:3.10-slim FROM python:3.10-slim
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1


# dipendenze native utili a numpy/scikit (safe choice)
# Installiamo solo libgomp1 (necessaria per scikit-learn)
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential gcc g++ \
libgomp1 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*


WORKDIR /app WORKDIR /app


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

# Correzione nome pacchetto e upgrade pip
RUN pip install --no-cache-dir --upgrade pip
RUN pip install streamlit==1.29.0 streamlit-drawable-canvas==0.9.3 PyYAML Pillow pandas
RUN pip install --no-cache-dir folium streamlit-folium
RUN pip install --no-cache-dir psutil
# Copia e installazione in un unico step per ridurre i layer
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt


# Copia del resto dell'applicazione
COPY app/ /app/app/ COPY app/ /app/app/
COPY entrypoint.sh /app/entrypoint.sh COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /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
# Setup utente e permessi
RUN useradd -m appuser && \
mkdir -p /home/appuser/data /home/appuser/models && \
chown -R appuser:appuser /home/appuser /app


USER appuser
ENTRYPOINT ["/app/entrypoint.sh"] ENTRYPOINT ["/app/entrypoint.sh"]

+ 1
- 1
README.md 查看文件

@@ -411,7 +411,7 @@ curl -k -X 'GET' \
docker compose -p ble-ai-localizer build docker compose -p ble-ai-localizer build
docker compose -p ble-ai-localizer up -d --build docker compose -p ble-ai-localizer up -d --build
docker system prune docker system prune
docker rmi ble-ai-localizer:0.1.0


Gestione Sart/Stop Container Gestione Sart/Stop Container
cd /data/service/ble-ai-localizer cd /data/service/ble-ai-localizer


+ 0
- 58
app/leaflet_bridge.js 查看文件

@@ -1,58 +0,0 @@
// leaflet_bridge.js
function initMap(config) {
const { imgUrl, width, height, meta, grid_size, dots } = config;

// Setup Coordinate XY (0,0 in alto a sinistra)
const map = L.map('map', {
crs: L.CRS.Simple,
minZoom: -2,
maxZoom: 4,
attributionControl: false
});

const bounds = [[-height, 0], [0, width]];
L.imageOverlay(imgUrl, bounds).addTo(map);
map.fitBounds(bounds);

// Gestione Griglia
let gridLayer = L.layerGroup();
if (meta.show_grid) {
const step = meta.grid_size || 100;
const S = meta.pixel_ratio || 1;
for (let x = 0; x <= width; x += (step * S)) {
L.polyline([[0, x], [-height, x]], {color: '#ccc', weight: 1, opacity: 0.5}).addTo(gridLayer);
}
for (let y = 0; y >= -height; y -= (step * S)) {
L.polyline([[y, 0], [y, width]], {color: '#ccc', weight: 1, opacity: 0.5}).addTo(gridLayer);
}
gridLayer.addTo(map);
}

// Visualizzazione punti esistenti (Verdi e Blu)
dots.forEach(dot => {
const color = dot.status === 'completed' ? '#228B22' : '#0000FF';
L.circleMarker([dot.y, dot.x], {
radius: config.dot_size / 5,
fillColor: color,
color: 'white',
weight: 2,
fillOpacity: 0.8
}).addTo(map).bindPopup(`X: ${dot.relX}, Y: ${dot.relY}`);
});

// Evento Click per rilievo
map.on('click', function(e) {
const raw_x = e.latlng.lng;
const raw_y = e.latlng.lat; // In Leaflet Simple, Y è negativa sotto l'origine

// Invio dati a Streamlit
window.parent.postMessage({
type: 'streamlit:setComponentValue',
value: {
x: raw_x,
y: raw_y,
timestamp: new Date().getTime()
}
}, '*');
});
}

+ 209
- 41
app/map_manager.py 查看文件

@@ -1,49 +1,217 @@
import os
import json
import streamlit as st
import pandas as pd
from PIL import Image
from pathlib import Path
import time
import folium
from streamlit_folium import st_folium
import math
import base64 import base64
from io import BytesIO from io import BytesIO


def get_image_base64(img):
# --- CACHE IMMAGINE ---
@st.cache_data
def get_image_base64(img_path):
img = Image.open(img_path).convert("RGBA")
w, h = img.size
buffered = BytesIO() buffered = BytesIO()
img.save(buffered, format="PNG") img.save(buffered, format="PNG")
img_str = base64.b64encode(buffered.getvalue()).decode()
return f"data:image/png;base64,{img_str}"
img_str = base64.b64encode(buffered.getvalue()).decode("ascii")
return f"data:image/png;base64,{img_str}", w, h


def show_mapper_v2(cfg):
# ... (caricamento meta e percorsi come nel tuo file originale) ...
img_path = MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}.png"
img = Image.open(img_path).convert("RGBA")
img_width, img_height = img.size
img_b64 = get_image_base64(img)
# --- FUNZIONE SALVATAGGIO FISICO ---
def force_save_json(path, data):
try:
with open(path, "w", encoding='utf-8') as f:
json.dump(data, f, indent=4)
return True
except Exception as e:
st.error(f"Errore scrittura disco: {e}")
return False


# Prepariamo la lista dei punti esistenti (Punto 6 delle specifiche)
dots_data = []
# Qui cicla sui tuoi file CSV e popola dots_data con {x, y, relX, relY, status}
# Integrazione del componente HTML
# Carichiamo il JS dal file esterno
with open("leaflet_bridge.js", "r") as f:
js_code = f.read()

html_content = f"""
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<div id="map" style="height: 600px; width: 100%;"></div>
<script>
{js_code}
initMap({{
imgUrl: "{img_b64}",
width: {img_width},
height: {img_height},
meta: {json.dumps(meta)},
dots: {json.dumps(dots_data)},
dot_size: {dot_size}
}});
</script>
"""
# Il componente restituisce il valore di window.parent.postMessage
result = components.html(html_content, height=650)
def show_mapper(cfg):
MAPS_DIR = Path(cfg['maps']['map_dir'])
SAMPLES_DIR = Path("/data/train/samples")
JOBS_BASE = Path(cfg['collect_train']['jobs_dir'])
PENDING_DIR = JOBS_BASE / "pending"
ERROR_DIR = JOBS_BASE / "error"
BEACONS_FILE = "/data/config/beacons.csv"
[p.mkdir(parents=True, exist_ok=True) for p in [MAPS_DIR, SAMPLES_DIR, PENDING_DIR, ERROR_DIR]]

# --- 1. GESTIONE UPLOAD ---
# La logica ora gestisce stringhe per floor_id permettendo il carattere "-"
maps = sorted([f.replace(cfg['maps']['floor_prefix'], "").split('.')[0]
for f in os.listdir(MAPS_DIR) if f.startswith(cfg['maps']['floor_prefix'])])
if result:
st.write(f"Posizione catturata: {result}")
# Qui inserisci la tua logica di salvataggio CSV che avevi nel punto 7
with st.expander("📂 Carica Nuova Planimetria", expanded=not maps):
c_up1, c_up2 = st.columns([1, 2])
new_f_id = c_up1.text_input("ID Piano (es. 0, -1):", key="new_f_id_v21")
up_file = c_up2.file_uploader("Immagine (PNG/JPG):", type=['png', 'jpg', 'jpeg'], key="up_v21")
if st.button("🚀 SALVA PIANO", use_container_width=True):
if new_f_id and up_file:
ext = Path(up_file.name).suffix
# Il nome file includerà il "-" se presente in new_f_id
img_save_path = MAPS_DIR / f"{cfg['maps']['floor_prefix']}{new_f_id}{ext}"
with open(img_save_path, "wb") as f:
f.write(up_file.getbuffer())
m_path = MAPS_DIR / f"{cfg['maps']['meta_prefix']}{new_f_id}.json"
force_save_json(m_path, {"pixel_ratio": 1.0, "calibrated": False, "origin": [0, 0], "grid_size": 50})
st.session_state.current_floor = new_f_id
st.rerun()

if not maps: return

# --- 2. RIGA STATO ---
c_p, c_s, c_o = st.columns([2.5, 2, 2])
with c_p:
s1, s2 = st.columns([1, 1.2])
s1.markdown("<p style='padding-top:35px; font-weight:bold;'>Piano Attivo:</p>", unsafe_allow_html=True)
def_idx = maps.index(st.session_state.current_floor) if st.session_state.get("current_floor") in maps else 0
floor_id = s2.selectbox("", maps, index=def_idx, key="f_v21", label_visibility="collapsed")
if st.session_state.get("prev_floor") != floor_id:
st.session_state.cal_points = []
st.session_state.temp_lat, st.session_state.temp_lng = None, None
st.session_state.prev_floor = floor_id

meta_path = MAPS_DIR / f"{cfg['maps']['meta_prefix']}{floor_id}.json"
if meta_path.exists():
with open(meta_path, "r") as f: meta = json.load(f)
else:
meta = {"pixel_ratio": 1.0, "calibrated": False, "origin": [0, 0]}

with c_s: st.info(f"📏 Scala: {'✅' if meta['calibrated'] else '❌'}\n({meta['pixel_ratio']:.4f} px/cm)")
with c_o: st.info(f"🎯 Origine: {'✅' if meta['origin'] != [0,0] else '❌'}\n(X:{meta['origin'][0]}, Y:{meta['origin'][1]})")

if not meta["calibrated"] or meta["origin"] == [0, 0]:
st.warning(f"💡 **Piano {floor_id} da configurare**: Esegui **Calibra** e imposta l'**Origine**.")

# --- 3. IMPOSTAZIONI GLOBALI (Default disabilitati) ---
st.markdown("---")
g1, g2, g3, g4 = st.columns([1.2, 1.5, 1.2, 2])
with g1:
# Griglia disattivata di default per evitare interferenze iniziali
show_grid = st.toggle("Griglia", value=False, key="grid_v21")
with g2:
grid_cm = st.select_slider("Passo (cm):", options=[25, 50, 100, 200], value=50, key="step_v21")
with g3:
# Stay Grid disattivato di default
snap_on = st.toggle("Stay Grid", value=False, key="snap_v21")
with g4:
m_size = st.slider("Dimensione Marker:", 5, 20, 8, key="msize_v21")
st.markdown("---")

# --- 4. TOOLSET ---
t1, t2, t3 = st.columns(3)
if t1.button("📏 CALIBRA", use_container_width=True): st.session_state.map_tool = "Calibra"; st.session_state.cal_points = []
if t2.button("🎯 SET ORIGINE", use_container_width=True): st.session_state.map_tool = "Origine"
if t3.button("📡 RILEVA", use_container_width=True): st.session_state.map_tool = "Rileva"
tool = st.session_state.get('map_tool', 'Rileva')

# --- 5. MAPPA ---
col_map, col_ui = st.columns([3, 1])
with col_map:
img_p = next((MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}{e}" for e in ['.png','.jpg','.jpeg'] if (MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}{e}").exists()))
img_data, w, h = get_image_base64(img_p)
bounds = [[0, 0], [h, w]]
m = folium.Map(location=[h/2, w/2], crs="Simple", tiles=None, attribution_control=False)
m.fit_bounds(bounds)
m.options.update({"minZoom": -6, "maxZoom": 6, "zoomSnap": 0.25, "maxBounds": bounds, "maxBoundsViscosity": 1.0})
folium.raster_layers.ImageOverlay(image=img_data, bounds=bounds, interactive=True).add_to(m)

# ORIGINE (Sempre visibile se impostata)
if meta["origin"] != [0, 0]:
ox, oy = meta["origin"]
folium.CircleMarker(location=[oy, ox], radius=6, color="black", fill=True, zindex=1000, tooltip="Origine (0,0)").add_to(m)

# GRIGLIA
if show_grid and meta["calibrated"] and meta["origin"] != [0, 0]:
px_step = grid_cm * meta["pixel_ratio"]
ox, oy = meta["origin"]
for x in sorted(list(range(int(ox), w, int(px_step))) + list(range(int(ox), 0, -int(px_step)))):
folium.PolyLine([[0, x], [h, x]], color="blue", weight=1, opacity=0.1).add_to(m)
for y in sorted(list(range(int(oy), h, int(px_step))) + list(range(int(oy), 0, -int(px_step)))):
folium.PolyLine([[y, 0], [y, w]], color="blue", weight=1, opacity=0.1).add_to(m)

# Disegno Punti Storici
def draw_points(directory, color, shape="circle"):
if not meta["calibrated"] or meta["origin"] == [0,0]: return
for f in Path(directory).glob("*.csv"):
try:
df = pd.read_csv(f, sep=";")
df.columns = [c.lower() for c in df.columns]
# Confronto tra stringhe per gestire piani negativi e ID complessi
if str(df.iloc[0].get('z', df.iloc[0].get('floor'))) == str(floor_id):
px_x = (df.iloc[0]['x'] * meta["pixel_ratio"]) + meta["origin"][0]
px_y = meta["origin"][1] - (df.iloc[0]['y'] * meta["pixel_ratio"])
if shape == "circle": folium.CircleMarker(location=[px_y, px_x], radius=m_size, color=color, fill=True, fill_opacity=0.8).add_to(m)
elif shape == "square": folium.RegularPolygonMarker(location=[px_y, px_x], number_of_sides=4, radius=m_size, color="black", weight=2, fill=True, fill_color=color).add_to(m)
elif shape == "diamond": folium.RegularPolygonMarker(location=[px_y, px_x], number_of_sides=4, rotation=45, radius=m_size, color="black", weight=1, fill=True, fill_color=color).add_to(m)
except: continue

draw_points(SAMPLES_DIR, "green", "circle")
draw_points(PENDING_DIR, "yellow", "diamond")
draw_points(ERROR_DIR, "red", "square")

# Feedback Visivo Click
if tool == "Calibra" and st.session_state.get("cal_points"):
for p in st.session_state.cal_points: folium.CircleMarker(location=p, radius=6, color="red", fill=True).add_to(m)
if len(st.session_state.cal_points) == 2: folium.PolyLine(st.session_state.cal_points, color="red", weight=3).add_to(m)
elif st.session_state.get("temp_lat") is not None:
folium.CircleMarker(location=[st.session_state.temp_lat, st.session_state.temp_lng], radius=m_size+2, color="blue", fill=True, zindex=500).add_to(m)

out = st_folium(m, height=600, width=None, key=f"map_v21_{floor_id}_{tool}")
click = out.get("last_clicked")
if click:
clat, clng = click["lat"], click["lng"]
if tool == "Calibra":
if len(st.session_state.get("cal_points", [])) < 2:
st.session_state.cal_points.append([clat, clng]); st.rerun()
else:
if tool == "Rileva" and snap_on and meta["calibrated"]:
px_step = grid_cm * meta["pixel_ratio"]
st.session_state.temp_lng = meta["origin"][0] + round((clng - meta["origin"][0]) / px_step) * px_step
st.session_state.temp_lat = meta["origin"][1] + round((clat - meta["origin"][1]) / px_step) * px_step
else:
st.session_state.temp_lat, st.session_state.temp_lng = clat, clng
st.rerun()

# --- 6. LOGICA UI ---
with col_ui:
st.write(f"### Tool: **{tool}**")
err_files = list(ERROR_DIR.glob("*.csv"))
if err_files and st.button(f"🗑️ PULISCI ERRORI", use_container_width=True):
for f in err_files: os.remove(f); st.rerun()
if tool == "Calibra":
pts = st.session_state.get("cal_points", [])
if len(pts) == 2:
dist_cm = st.number_input("Distanza reale (cm):", value=100.0)
if st.button("📏 SALVA SCALA", use_container_width=True, type="primary"):
px_d = math.sqrt((pts[1][1]-pts[0][1])**2 + (pts[1][0]-pts[0][0])**2)
meta.update({"pixel_ratio": px_d / dist_cm, "calibrated": True})
if force_save_json(meta_path, meta): st.session_state.cal_points = []; st.rerun()
elif st.session_state.get("temp_lat") is not None:
px_x, px_y = st.session_state.temp_lng, st.session_state.temp_lat
if tool == "Origine":
st.metric("X (px)", int(px_x)); st.metric("Y (px)", int(px_y))
if st.button("💾 SALVA ORIGINE", use_container_width=True, type="primary"):
meta["origin"] = [int(px_x), int(px_y)]
if force_save_json(meta_path, meta): st.rerun()
elif tool == "Rileva" and meta["calibrated"]:
rx = (px_x - meta["origin"][0]) / meta["pixel_ratio"]
ry = (meta["origin"][1] - px_y) / meta["pixel_ratio"]
sx, sy = int(round(rx)), int(round(ry))
st.metric("X (cm)", sx); st.metric("Y (cm)", sy)
if os.path.exists(BEACONS_FILE):
b_df = pd.read_csv(BEACONS_FILE, sep=";")
sel_b = st.selectbox("Beacon:", b_df.apply(lambda x: f"{x['BeaconName']} | {x['MAC']}", axis=1))
if st.button("🚀 REGISTRA", use_container_width=True, type="primary"):
b_name, b_mac = sel_b.split(" | ")
id_str = f"{b_name}_{floor_id}_{sx}_{sy}"
# Salvataggio Z come stringa per mantenere il "-"
data = {"Position": id_str, "Floor": floor_id, "RoomName": b_name, "X": sx, "Y": sy, "Z": floor_id, "BeaconName": b_name, "MAC": b_mac}
pd.DataFrame([data]).to_csv(PENDING_DIR / f"{id_str}.csv", sep=";", index=False); st.rerun()

+ 0
- 145
app/map_manager.py_v1 查看文件

@@ -1,145 +0,0 @@
import os
import json
import streamlit as st
import pandas as pd
from PIL import Image, ImageDraw
from pathlib import Path
from streamlit_drawable_canvas import st_canvas
import time

def show_mapper(cfg):
# --- 1. CONFIGURAZIONE PERCORSI ---
MAPS_DIR = Path(cfg['maps']['map_dir'])
SAMPLES_DIR = Path(cfg['train']['samples_dir'])
PENDING_DIR = Path(cfg['collect_train']['jobs_dir']) / "pending"
BEACONS_FILE = "/data/config/beacons.csv"

[p.mkdir(parents=True, exist_ok=True) for p in [MAPS_DIR, SAMPLES_DIR, PENDING_DIR]]

def load_map_metadata(f_id):
meta_path = MAPS_DIR / f"{cfg['maps']['meta_prefix']}{f_id}.json"
defaults = {"pixel_ratio": 1.0, "calibrated": False, "origin": [0, 0], "grid_size": 50}
if meta_path.exists():
with open(meta_path, "r") as f: return {**defaults, **json.load(f)}
return defaults

st.subheader("🗺️ Gestione Mappatura e Rilevamento")

# Selezione Piano
maps = sorted([f.replace(cfg['maps']['floor_prefix'], "").split('.')[0] for f in os.listdir(MAPS_DIR) if f.startswith(cfg['maps']['floor_prefix'])])
if not maps: st.error("Nessuna mappa trovata."); return
floor_id = st.selectbox("Seleziona Piano (Z):", maps)
meta = load_map_metadata(floor_id)

# --- 2. STATO SISTEMA ---
c_s1, c_s2 = st.columns(2)
with c_s1:
st.info(f"📏 Scala: {'✅' if meta['calibrated'] else '❌'} ({meta['pixel_ratio']:.4f} px/cm)")
with c_s2:
st.info(f"🎯 Origine: {'✅' if meta['origin'] != [0,0] else '❌'} (X:{meta['origin'][0]}, Y:{meta['origin'][1]})")

# --- 3. CONTROLLI VISUALIZZAZIONE ---
with st.expander("🎨 Opzioni Visualizzazione", expanded=True):
c_v1, c_v2, c_v3 = st.columns(3)
zoom = c_v1.slider("🔍 Zoom Mappa", 0.1, 4.0, 1.0, 0.1)
dot_size = c_v2.slider("🔵/🟢 Dimensione Punti", 10, 100, 40)
mode = c_v3.radio("Modalità Interazione:", ["🖐️ NAVIGA", "🎯 AZIONE"], horizontal=True)

# --- 4. SELEZIONE STRUMENTO ---
t1, t2, t3 = st.columns(3)
if t1.button("📏 IMPOSTA SCALA", use_container_width=True): st.session_state.map_tool = "Calibra"
if t2.button("🎯 SET ORIGINE", use_container_width=True): st.session_state.map_tool = "Origine"
is_ready = meta.get("calibrated", False) and meta["origin"] != [0, 0]
if t3.button("📡 RILEVA", use_container_width=True, disabled=not is_ready): st.session_state.map_tool = "Rileva"

tool = st.session_state.get('map_tool', 'Rileva')
st.write(f"Strumento attivo: **{tool}**")

# --- 5. PREPARAZIONE IMMAGINE E STORICO ---
img_path = MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}.png"
if not img_path.exists(): img_path = MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}.jpg"
img = Image.open(img_path).convert("RGBA")
draw = ImageDraw.Draw(img)

if is_ready:
samples_files = list(SAMPLES_DIR.glob(f"{floor_id}_*.csv"))
pending_files = list(PENDING_DIR.glob(f"{floor_id}_*.csv"))
def draw_points(files, color):
for f in files:
try:
p = f.stem.split("_")
px = (int(p[1]) * meta["pixel_ratio"]) + meta["origin"][0]
py = (int(p[2]) * meta["pixel_ratio"]) + meta["origin"][1]
r = dot_size // 2
draw.ellipse([px-r, py-r, px+r, py+r], fill=color, outline="white", width=2)
except: continue

draw_points(samples_files, "#228B22") # Verde: Completati
draw_points(pending_files, "#0000FF") # Blu: In corso

# --- 6. CANVAS ---
d_mode = "transform" if mode == "🖐️ NAVIGA" else ("line" if tool == "Calibra" else "point")
canvas_result = st_canvas(
background_image=img,
height=int(img.size[1] * zoom),
width=int(img.size[0] * zoom),
drawing_mode=d_mode,
display_toolbar=True,
update_streamlit=True,
key=f"canvas_v10_{floor_id}_{zoom}_{mode}_{tool}",
)

# --- 7. LOGICA AZIONI E SALVATAGGIO CSV ---
if mode == "🎯 AZIONE" and canvas_result.json_data and canvas_result.json_data["objects"]:
last = canvas_result.json_data["objects"][-1]
raw_x, raw_y = last["left"] / zoom, last["top"] / zoom

if tool == "Calibra" and last["type"] == "line":
px_dist = ((last["x2"] - last["x1"])**2 + (last["y2"] - last["y1"])**2)**0.5 / zoom
dist_cm = st.number_input("Distanza reale (cm):", value=100.0)
if st.button("APPLICA SCALA"):
meta.update({"pixel_ratio": px_dist / dist_cm, "calibrated": True})
with open(MAPS_DIR / f"{cfg['maps']['meta_prefix']}{floor_id}.json", "w") as f:
json.dump(meta, f)
st.rerun()

elif tool == "Origine":
if st.button(f"Fissa Origine qui: {int(raw_x)}, {int(raw_y)}"):
meta["origin"] = [int(raw_x), int(raw_y)]
with open(MAPS_DIR / f"{cfg['maps']['meta_prefix']}{floor_id}.json", "w") as f:
json.dump(meta, f)
st.rerun()

elif tool == "Rileva" and is_ready:
dx = (raw_x - meta["origin"][0]) / meta["pixel_ratio"]
dy = (raw_y - meta["origin"][1]) / meta["pixel_ratio"]
grid = meta.get("grid_size", 50)
sx, sy = int(round(dx/grid)*grid), int(round(dy/grid)*grid)
st.write(f"### 📍 Punto: {sx}cm, {sy}cm")
if os.path.exists(BEACONS_FILE):
b_df = pd.read_csv(BEACONS_FILE, sep=";")
sel_b = st.selectbox("Seleziona Beacon:", b_df.apply(lambda x: f"{x['BeaconName']} | {x['MAC']}", axis=1))
if st.button("🚀 REGISTRA LETTURA", type="primary", use_container_width=True):
b_name, b_mac = sel_b.split(" | ")
fname = f"{floor_id}_{sx}_{sy}.csv"
# --- FIX SINTASSI CSV ---
# Formato: Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
data_out = {
"Position": f"P_{sx}_{sy}",
"Floor": floor_id,
"RoomName": "Area_Rilevazione",
"X": sx,
"Y": sy,
"Z": 0,
"BeaconName": b_name,
"MAC": b_mac
}
pd.DataFrame([data_out]).to_csv(PENDING_DIR / fname, index=False, sep=";")
st.toast(f"Punto Blu registrato!")
time.sleep(1); st.rerun()

+ 0
- 49
app/map_manager.py_v2 查看文件

@@ -1,49 +0,0 @@
import base64
from io import BytesIO

def get_image_base64(img):
buffered = BytesIO()
img.save(buffered, format="PNG")
img_str = base64.b64encode(buffered.getvalue()).decode()
return f"data:image/png;base64,{img_str}"

def show_mapper_v2(cfg):
# ... (caricamento meta e percorsi come nel tuo file originale) ...
img_path = MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}.png"
img = Image.open(img_path).convert("RGBA")
img_width, img_height = img.size
img_b64 = get_image_base64(img)

# Prepariamo la lista dei punti esistenti (Punto 6 delle specifiche)
dots_data = []
# Qui cicla sui tuoi file CSV e popola dots_data con {x, y, relX, relY, status}
# Integrazione del componente HTML
# Carichiamo il JS dal file esterno
with open("leaflet_bridge.js", "r") as f:
js_code = f.read()

html_content = f"""
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<div id="map" style="height: 600px; width: 100%;"></div>
<script>
{js_code}
initMap({{
imgUrl: "{img_b64}",
width: {img_width},
height: {img_height},
meta: {json.dumps(meta)},
dots: {json.dumps(dots_data)},
dot_size: {dot_size}
}});
</script>
"""
# Il componente restituisce il valore di window.parent.postMessage
result = components.html(html_content, height=650)
if result:
st.write(f"Posizione catturata: {result}")
# Qui inserisci la tua logica di salvataggio CSV che avevi nel punto 7

+ 83
- 81
app/web_inference.py 查看文件

@@ -1,100 +1,102 @@
import streamlit as st import streamlit as st
import pandas as pd import pandas as pd
import os
import json import json
from PIL import Image, ImageDraw, ImageFont
import folium
from streamlit_folium import st_folium
from pathlib import Path from pathlib import Path
import time
import os
from PIL import Image
import base64
from io import BytesIO

# --- UTILS ---
@st.cache_data
def get_image_base64(img_path):
img = Image.open(img_path).convert("RGBA")
w, h = img.size
buffered = BytesIO()
img.save(buffered, format="PNG")
img_str = base64.b64encode(buffered.getvalue()).decode("ascii")
return f"data:image/png;base64,{img_str}", w, h


def show_inference_page(cfg): def show_inference_page(cfg):
# Definizione percorsi
# Assumendo che il file sia in /app/app/web_inference.py,
# cerchiamo il font nella stessa cartella 'app'
BASE_DIR = Path(__file__).parent
st.subheader("📡 Monitoraggio Beacon Real-Time")
# --- CONFIGURAZIONE PERCORSI ---
MAPS_DIR = Path(cfg['maps']['map_dir']) MAPS_DIR = Path(cfg['maps']['map_dir'])
INFER_FILE = Path(cfg['infer']['output_dir']) / cfg['infer']['out_file']
INFER_FILE = Path("/data/infer/infer.csv")
# Percorso del font locale
FONT_PATH = BASE_DIR / "DejaVuSans-Bold.ttf"

st.subheader("🤖 Monitoraggio Inferenza Live")
# --- 1. SELEZIONE E STATO (RIGA COMPATTA) ---
maps = sorted([f.replace(cfg['maps']['floor_prefix'], "").split('.')[0]
for f in os.listdir(MAPS_DIR) if f.startswith(cfg['maps']['floor_prefix'])])
if not maps:
st.warning("Nessuna mappa configurata.")
return


# --- CONTROLLI NEL TAB ---
with st.expander("🎨 Opzioni e Controllo", expanded=True):
col_ctrl1, col_ctrl2, col_ctrl3, col_ctrl4 = st.columns(4)
with col_ctrl1:
dot_size = st.slider("Dimensione Marker", 10, 100, 45, key="inf_dot")
with col_ctrl2:
refresh_rate = st.select_slider("Refresh (s)", options=[2, 5, 10, 30], value=5, key="inf_ref")
with col_ctrl3:
show_labels = st.checkbox("Mostra MAC", value=True, key="inf_show_mac")
with col_ctrl4:
# Monitoraggio disattivato di default per evitare login fantasma
auto_refresh = st.toggle("🔄 Monitoraggio Attivo", value=False, key="inf_auto")
# Lettura dati per conteggio
df_infer = pd.DataFrame()
if INFER_FILE.exists():
df_infer = pd.read_csv(INFER_FILE, sep=";")


floor_id = st.number_input("Piano (Z)", value=0, min_value=0, step=1, key="inf_z")
# Layout riga 1
c_piano, c_count, c_size = st.columns([3, 2, 2])
with c_piano:
sub1, sub2 = st.columns([1, 1.2])
sub1.markdown("<p style='padding-top:35px; font-weight:bold; font-size:15px;'>Piano Visualizzato:</p>", unsafe_allow_html=True)
floor_id = sub2.selectbox("", maps, key="inf_floor_v24", label_visibility="collapsed")
# Filtro dati per il piano scelto
df_active = df_infer[(df_infer['z'].astype(str) == str(floor_id)) & (df_infer['x'] != -1)] if not df_infer.empty else pd.DataFrame()

with c_count:
st.info(f"📡 Beacon Attivi: **{len(df_active)}**\n(Totali nel file: {len(df_infer)})")

with c_size:
# Slider per dimensione pallini (come nel mapper)
m_size = st.slider("Dimensione Beacon:", 5, 20, 8, key="inf_msize_v24")

# Caricamento Metadati # Caricamento Metadati
meta_path = MAPS_DIR / f"{cfg['maps']['meta_prefix']}{floor_id}.json" meta_path = MAPS_DIR / f"{cfg['maps']['meta_prefix']}{floor_id}.json"
meta = {"pixel_ratio": 1.0, "origin": [0, 0]}
if meta_path.exists():
with open(meta_path, "r") as f:
meta.update(json.load(f))
if not meta_path.exists(): return
with open(meta_path, "r") as f: meta = json.load(f)

# --- 2. RENDERING MAPPA ---
st.markdown("---")
img_p = next((MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}{e}" for e in ['.png','.jpg'] if (MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}{e}").exists()))
img_data, w, h = get_image_base64(img_p)
bounds = [[0, 0], [h, w]]
m = folium.Map(location=[h/2, w/2], crs="Simple", tiles=None, attribution_control=False)
m.fit_bounds(bounds)
m.options.update({"minZoom": -6, "maxZoom": 6, "zoomSnap": 0.25, "maxBounds": bounds, "maxBoundsViscosity": 1.0})
folium.raster_layers.ImageOverlay(image=img_data, bounds=bounds).add_to(m)


try:
if INFER_FILE.exists():
df = pd.read_csv(INFER_FILE, sep=";")
df_active = df[(df['z'] == floor_id) & (df['x'] != -1) & (df['y'] != -1)]
img_path = MAPS_DIR / f"{cfg['maps']['floor_prefix']}{floor_id}.png"
if img_path.exists():
img = Image.open(img_path).convert("RGBA")
draw = ImageDraw.Draw(img)
# Disegno Beacon
if meta["calibrated"] and meta["origin"] != [0,0]:
# Origine
folium.CircleMarker(location=[meta["origin"][1], meta["origin"][0]], radius=4, color="black", fill=True).add_to(m)


# --- GESTIONE FONT XL ---
# Dimensione proporzionale al pallino (1.5x)
dynamic_font_size = int(dot_size * 1.5)
font = None
if FONT_PATH.exists():
try:
# Tenta il caricamento del file TTF locale
font = ImageFont.truetype(str(FONT_PATH), dynamic_font_size)
except Exception as e:
# Se il file è corrotto (unknown file format), mostra errore e usa fallback
st.error(f"Errore caricamento font: {e}. Controlla il file DejaVuSans-Bold.ttf")
if font is None:
font = ImageFont.load_default()
for _, row in df_active.iterrows():
px_x = (row['x'] * meta["pixel_ratio"]) + meta["origin"][0]
px_y = meta["origin"][1] - (row['y'] * meta["pixel_ratio"])
mac_label = str(row['mac'])[-5:]


for _, row in df_active.iterrows():
px_x = (row['x'] * meta["pixel_ratio"]) + meta["origin"][0]
px_y = (row['y'] * meta["pixel_ratio"]) + meta["origin"][1]
folium.CircleMarker(
location=[px_y, px_x], radius=m_size, color="blue",
fill=True, fill_color="cyan", fill_opacity=0.8
).add_to(m)


r = dot_size
# Disegno Marker
draw.ellipse([px_x-(r+5), px_y-(r+5), px_x+(r+5), px_y+(r+5)], fill="black")
draw.ellipse([px_x-r, px_y-r, px_x+r, px_y+r], fill="#00E5FF")
if show_labels:
label = f"{str(row['mac'])[-5:]}"
# Posizionamento dinamico a destra del pallino
text_x = px_x + r + 20
text_y = px_y - (dynamic_font_size / 2)
# Outline per leggibilità (bordo nero spesso 3px)
for dx, dy in [(-3,-3), (3,-3), (-3,3), (3,3), (0,-3), (0,3), (-3,0), (2,0)]:
draw.text((text_x + dx, text_y + dy), label, font=font, fill="black")
# Testo Giallo
draw.text((text_x, text_y), label, font=font, fill="#FFFF00")
folium.Marker(
location=[px_y, px_x],
icon=folium.DivIcon(html=f"""<div style="font-family: sans-serif; color: black; font-weight: bold; font-size: {int(m_size*1.2)}pt; width: 80px; transform: translate({m_size+2}px, -{m_size+2}px);">{mac_label}</div>""")
).add_to(m)


st.image(img, use_column_width=True)
st.metric("Dispositivi Online", len(df_active))
except Exception as e:
st.error(f"Errore generale: {e}")
st_folium(m, height=700, width=None, key=f"inf_map_v24_{floor_id}", use_container_width=True)


# --- REFRESH CONDIZIONALE ---
if auto_refresh:
time.sleep(refresh_rate)
st.rerun()
# --- 3. TABELLA RIEPILOGO COMPLETA ---
if not df_infer.empty:
st.subheader("Dettaglio Dispositivi (Tutti i Piani)")
# Aggiunta colonna Z per chiarezza
st.dataframe(df_infer[['mac', 'z', 'x', 'y']], use_container_width=True)

+ 4
- 1
app/web_suite.py 查看文件

@@ -27,7 +27,7 @@ st.set_page_config(
menu_items={ menu_items={
'Get Help': None, 'Get Help': None,
'Report a bug': None, 'Report a bug': None,
'About': "# ▒~_~[▒▒~O BLE AI Localizer - Suite\nSistema professionale di posizionamento BLE."
'About': "🛰️BLE AI Localizer - Suite\nSistema professionale di posizionamento BLE."
} }
) )


@@ -44,6 +44,9 @@ st.markdown("""
</style> </style>
""", unsafe_allow_html=True) """, unsafe_allow_html=True)


if "main_login_user" not in st.session_state:
st.session_state["main_login_user"] = ""

# --- LOGIN (Tua versione originale) --- # --- LOGIN (Tua versione originale) ---
if "password_correct" not in st.session_state: if "password_correct" not in st.session_state:
st.session_state["password_correct"] = False st.session_state["password_correct"] = False


+ 4
- 4
config/config.yaml 查看文件

@@ -63,18 +63,18 @@ infer:
window_seconds: 5 window_seconds: 5
xy_round: 0 xy_round: 0
maps: maps:
default_dot_size: 20
default_grid_size: 100
floor_prefix: floor_ floor_prefix: floor_
map_dir: /data/maps map_dir: /data/maps
meta_prefix: meta_ meta_prefix: meta_
default_grid_size: 100 # Dimensione griglia predefinita in cm
default_dot_size: 20 # Dimensione predefinita dei marker
show_grid_default: true # Stato iniziale della griglia
show_grid_default: true
ml: ml:
k: 5 k: 5
method: knn method: knn
metric: euclidean metric: euclidean
weights: distance weights: distance
mode: infer
mode: collect_train
mqtt: mqtt:
ca_file: '' ca_file: ''
client_id: ble-ai-localizer client_id: ble-ai-localizer


+ 9
- 9
data/infer/infer.csv 查看文件

@@ -1,13 +1,13 @@
mac;z;x;y mac;z;x;y
C8:3F:8F:17:DB:35;1;1061;1290
C8:3F:8F:17:DB:35;1;1058;1294
C3:00:00:39:47:DF;-1;-1;-1 C3:00:00:39:47:DF;-1;-1;-1
C3:00:00:39:47:C4;1;1386;1501
C3:00:00:39:47:C4;1;1304;1478
C3:00:00:39:47:E2;-1;-1;-1 C3:00:00:39:47:E2;-1;-1;-1
C7:AE:56:1E:38:B7;1;1311;1501
C7:AE:56:1E:38:B7;1;1323;1499
E0:1F:9A:7A:47:D2;-1;-1;-1 E0:1F:9A:7A:47:D2;-1;-1;-1
C3:00:00:57:B9:D9;1;1060;1380
C3:00:00:57:B9:DB;1;1056;1383
C3:00:00:57:B9:F4;1;1061;1292
C3:00:00:57:B9:DC;1;1060;1294
C3:00:00:57:B9:DD;1;1216;1444
C3:00:00:57:B9:DF;1;1059;1296
C3:00:00:57:B9:D9;1;1058;1382
C3:00:00:57:B9:DB;1;1060;1293
C3:00:00:57:B9:F4;1;1059;1296
C3:00:00:57:B9:DC;1;1060;1291
C3:00:00:57:B9:DD;1;1203;1452
C3:00:00:57:B9:DF;1;1061;1294

二進制
data/maps.bck/floor_0.png 查看文件

Before After
Width: 8337  |  Height: 4803  |  Size: 528 KiB

二進制
data/maps.bck/floor_1.png 查看文件

Before After
Width: 8238  |  Height: 4672  |  Size: 556 KiB

+ 1
- 0
data/maps.bck/meta_0.json 查看文件

@@ -0,0 +1 @@
{"pixel_ratio": 1.5843674344166205, "calibrated": true, "origin": [1296, 3744], "grid_size": 50, "show_grid": true}

+ 1
- 0
data/maps.bck/meta_1.json 查看文件

@@ -0,0 +1 @@
{"pixel_ratio": 1.5919341046277666, "calibrated": true, "origin": [1251, 1143], "grid_size": 50}

二進制
data/maps.old/floor_0.png 查看文件

Before After
Width: 8337  |  Height: 4803  |  Size: 528 KiB

二進制
data/maps.old/floor_1.png 查看文件

Before After
Width: 8238  |  Height: 4672  |  Size: 556 KiB

+ 9
- 0
data/maps.old/meta_0.json 查看文件

@@ -0,0 +1,9 @@
{
"pixel_ratio": 1.584476207254857,
"calibrated": true,
"origin": [
1248,
3790
],
"grid_size": 50
}

+ 1
- 0
data/maps.old/meta_1.json 查看文件

@@ -0,0 +1 @@
{"pixel_ratio": 1.5924732396126005, "calibrated": true, "origin": [1297, 3586], "grid_size": 50}

二進制
data/maps/floor_-1.png 查看文件

Before After
Width: 8238  |  Height: 4672  |  Size: 556 KiB

+ 9
- 0
data/maps/meta_-1.json 查看文件

@@ -0,0 +1,9 @@
{
"pixel_ratio": 1.5920551746472242,
"calibrated": true,
"origin": [
1297,
3587
],
"grid_size": 50
}

+ 9
- 1
data/maps/meta_0.json 查看文件

@@ -1 +1,9 @@
{"pixel_ratio": 1.574668658056015, "calibrated": true, "origin": [1193, 1031], "grid_size": 50, "show_grid": true}
{
"pixel_ratio": 1.584773230620443,
"calibrated": true,
"origin": [
1247,
3791
],
"grid_size": 50
}

+ 9
- 1
data/maps/meta_1.json 查看文件

@@ -1 +1,9 @@
{"pixel_ratio": 1.5919341046277666, "calibrated": true, "origin": [1251, 1143], "grid_size": 50}
{
"pixel_ratio": 1.5924129560451799,
"calibrated": true,
"origin": [
1297,
3587
],
"grid_size": 50
}

data/train/jobs/pending/1_1600_450.csv → data/train/jobs/done/1_1600_450.csv 查看文件


+ 2
- 0
data/train/jobs/done/BC-21_-1_1050_250.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_-1_1050_250;-1;BC-21;1050;250;-1;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_0_195_1425.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_0_195_1425;0;BC-21;195;1425;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_0_2114_796.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_0_2114_796;0;BC-21;2114;796;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_0_2115_697.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
P_2115_697;0;BC-21;2115;697;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_0_2568_946.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_0_2568_946;0;BC-21;2568;946;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_0_400_100.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_0_400_100;0;BC-21;400;100;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_0_619_122.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_0_619_122;0;BC-21;619;122;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_1_1050_500.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_1_1050_500;1;BC-21;1050;500;1;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-21_1_950_750.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-21_1_950_750;1;BC-21;950;750;1;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/BC-22_-1_1650_1100.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-22_-1_1650_1100;-1;BC-22;1650;1100;-1;BC-22;C3:00:00:57:B9:D4

+ 2
- 0
data/train/jobs/done/BC-22_-1_170_1418.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-22_-1_170_1418;-1;BC-22;170;1418;-1;BC-22;C3:00:00:57:B9:D4

+ 2
- 0
data/train/jobs/done/BC-22_0_534_999.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-22_0_534_999;0;BC-22;534;999;0;BC-22;C3:00:00:57:B9:D4

+ 2
- 0
data/train/jobs/done/BC-23_0_1580_180.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-23_0_1580_180;0;BC-23;1580;180;0;BC-23;C3:00:00:57:B9:E8

+ 2
- 0
data/train/jobs/done/BC-23_1_130_1386.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-23_1_130_1386;1;BC-23;130;1386;1;BC-23;C3:00:00:57:B9:E8

+ 2
- 0
data/train/jobs/done/BC-23_1_1800_600.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-23_1_1800_600;1;BC-23;1800;600;1;BC-23;C3:00:00:57:B9:E8

+ 2
- 0
data/train/jobs/done/BC-24_1_1900_1400.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-24_1_1900_1400;1;BC-24;1900;1400;1;BC-24;C3:00:00:57:B9:F1

+ 2
- 0
data/train/jobs/done/BC-24_1_2200_1000.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-24_1_2200_1000;1;BC-24;2200;1000;1;BC-24;C3:00:00:57:B9:F1

+ 2
- 0
data/train/jobs/done/BC-25_0_562_239.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-25_0_562_239;0;BC-25;562;239;0;BC-25;C3:00:00:57:B9:E7

+ 2
- 0
data/train/jobs/done/BC-25_1_1450_600.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-25_1_1450_600;1;BC-25;1450;600;1;BC-25;C3:00:00:57:B9:E7

+ 2
- 0
data/train/jobs/done/BC-25_1_2400_300.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-25_1_2400_300;1;BC-25;2400;300;1;BC-25;C3:00:00:57:B9:E7

+ 2
- 0
data/train/jobs/done/BC-25_1_2500_1300.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
BC-25_1_2500_1300;1;BC-25;2500;1300;1;BC-25;C3:00:00:57:B9:E7

+ 2
- 0
data/train/jobs/done/P_1356_1256.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
P_1356_1256;0;BC-21;1356;1256;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/P_2142_616.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
P_2142_616;0;BC-21;2142;616;0;BC-21;C3:00:00:57:B9:E6

+ 2
- 0
data/train/jobs/done/P_2491_543.csv 查看文件

@@ -0,0 +1,2 @@
Position;Floor;RoomName;X;Y;Z;BeaconName;MAC
P_2491_543;0;BC-22;2491;543;0;BC-22;C3:00:00:57:B9:D4

+ 2
- 0
data/train/samples/-1_1050_250.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;1050;250;-1;-72.000;-82.000;-75.000;nan;nan;nan;nan;nan;-80.000;-51.500;-74.000;-64.000;-73.000;-77.000;nan;-76.000

+ 2
- 0
data/train/samples/-1_1650_1100.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:D4;1650;1100;-1;-77.000;nan;-78.000;nan;nan;nan;nan;nan;-76.000;nan;-75.000;-67.000;-76.000;-78.000;-83.000;-73.500

+ 2
- 0
data/train/samples/-1_170_1418.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:D4;170;1418;-1;-77.000;nan;-79.000;nan;-85.000;nan;nan;nan;-74.000;nan;-73.000;-67.000;-76.000;-78.000;-83.000;-74.000

+ 2
- 0
data/train/samples/0_1356_1256.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;1356;1256;0;-72.000;-82.000;-75.500;nan;nan;nan;nan;nan;-80.000;-51.000;-74.000;-64.500;-70.000;-79.000;nan;-75.500

+ 2
- 0
data/train/samples/0_1580_180.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E8;1580;180;0;-80.000;-82.000;-83.500;nan;-85.500;nan;nan;nan;-76.000;-59.000;-71.000;-64.000;-77.000;-78.000;-85.000;-74.000

+ 2
- 0
data/train/samples/0_1600_450.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;1600;450;0;-72.000;-82.000;-75.000;nan;nan;nan;nan;nan;-81.000;-51.500;-75.000;-64.000;-73.000;-77.500;nan;-76.000

+ 2
- 0
data/train/samples/0_195_1425.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;195;1425;0;-76.000;-82.000;-75.000;nan;nan;nan;nan;nan;-80.000;-52.000;-74.000;-64.000;-70.000;-78.000;nan;-76.000

+ 2
- 0
data/train/samples/0_2114_796.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;2114;796;0;-72.000;-82.000;-76.000;nan;nan;nan;nan;nan;-80.000;-52.000;-75.000;-64.000;-70.500;-78.000;nan;-75.000

+ 2
- 0
data/train/samples/0_2115_697.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;2115;697;0;-76.000;-82.000;-76.000;nan;nan;nan;nan;nan;-80.000;-52.000;-75.000;-64.000;-71.000;-77.000;nan;-76.000

+ 2
- 0
data/train/samples/0_2142_616.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;2142;616;0;-77.000;-82.000;-76.000;nan;nan;nan;nan;nan;-80.000;-52.000;-75.000;-64.000;-72.000;-78.000;nan;-76.000

+ 2
- 0
data/train/samples/0_2491_543.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:D4;2491;543;0;-77.000;nan;-79.000;nan;-84.000;nan;nan;nan;-74.000;nan;-75.000;-67.000;-77.000;-78.000;-83.000;-73.000

+ 2
- 0
data/train/samples/0_2568_946.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;2568;946;0;-76.000;-83.000;-76.000;nan;nan;nan;nan;nan;-80.000;-51.000;-74.500;-64.000;-73.000;-77.000;nan;-76.000

+ 2
- 0
data/train/samples/0_400_100.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;400;100;0;-77.000;-83.000;-75.000;nan;nan;nan;nan;nan;-80.000;-51.000;-76.000;-64.000;-73.000;-78.000;nan;-76.000

+ 2
- 0
data/train/samples/0_534_999.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:D4;534;999;0;-77.000;nan;-79.000;nan;-84.000;nan;nan;nan;-76.000;nan;-73.500;-66.000;-76.000;-78.000;-83.000;-73.000

+ 2
- 0
data/train/samples/0_562_239.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E7;562;239;0;-83.500;nan;-83.000;nan;nan;nan;nan;nan;-78.000;-48.000;-71.000;-75.000;-74.000;-78.500;-83.000;-80.000

+ 2
- 0
data/train/samples/0_619_122.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;619;122;0;-72.000;-82.000;-75.000;nan;nan;nan;nan;nan;-80.000;-52.000;-75.500;-64.000;-73.000;-78.000;nan;-76.000

+ 2
- 0
data/train/samples/1_1050_400.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:F1;1050;400;1;nan;nan;nan;nan;nan;nan;nan;nan;-77.000;nan;-69.000;-65.000;-71.000;-77.000;-82.000;-81.000

+ 2
- 0
data/train/samples/1_1050_500.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;1050;500;1;-76.000;-82.000;-75.000;nan;nan;nan;nan;nan;-80.000;-51.000;-75.000;-64.000;-73.000;-77.000;nan;-76.000

+ 2
- 0
data/train/samples/1_130_1386.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E8;130;1386;1;-80.000;-82.000;-84.000;nan;-85.000;nan;nan;nan;-76.000;-59.000;-71.000;-64.000;-77.000;-78.000;-85.000;-74.000

+ 2
- 0
data/train/samples/1_1450_600.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E7;1450;600;1;-83.500;nan;-83.000;nan;nan;nan;nan;nan;-79.000;-48.000;-71.000;-75.000;-77.000;-77.000;-83.000;-80.000

+ 2
- 0
data/train/samples/1_1800_600.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E8;1800;600;1;-80.000;-82.000;-83.000;nan;nan;nan;nan;nan;-76.000;-59.000;-71.000;-64.000;-76.500;-79.000;-84.500;-72.000

+ 2
- 0
data/train/samples/1_1900_1400.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:F1;1900;1400;1;nan;nan;nan;nan;nan;nan;nan;nan;-77.000;nan;-69.000;-65.000;-71.000;-77.000;-82.000;-81.000

+ 2
- 0
data/train/samples/1_2200_1000.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:F1;2200;1000;1;nan;nan;nan;nan;nan;nan;nan;nan;-77.000;nan;-69.000;-65.000;-71.000;-78.000;-82.000;-81.000

+ 2
- 0
data/train/samples/1_2400_300.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E7;2400;300;1;-83.000;nan;-83.000;nan;nan;nan;nan;nan;-78.000;-48.000;-71.000;-76.500;-77.000;-77.000;-83.000;-80.000

+ 2
- 0
data/train/samples/1_2500_1300.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E7;2500;1300;1;-84.000;nan;-83.000;nan;nan;nan;nan;nan;-79.000;-48.000;-71.000;-77.000;-77.000;-77.500;-83.000;-80.000

+ 2
- 0
data/train/samples/1_2750_200.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;2750;200;1;-72.000;-83.000;-76.000;nan;nan;nan;nan;nan;-80.000;-51.000;-74.500;-64.000;-70.000;-78.000;nan;-76.000

+ 2
- 0
data/train/samples/1_950_750.csv 查看文件

@@ -0,0 +1,2 @@
mac;x;y;z;AC:23:3F:C1:DD:3C;AC:23:3F:C1:DD:49;AC:23:3F:C1:DC:EE;AC:23:3F:C1:DD:40;AC:23:3F:C1:DD:51;AC:23:3F:C1:DD:48;AC:23:3F:C1:DD:50;AC:23:3F:C1:DC:D3;AC:23:3F:C1:DD:55;AC:23:3F:C1:DC:D1;AC:23:3F:C1:DC:CB;AC:23:3F:C1:DC:D2;AC:23:3F:C1:DD:31;AC:23:3F:C1:DD:4B;AC:23:3F:C1:DD:4E;AC:23:3F:C1:DC:CD
C3:00:00:57:B9:E6;950;750;1;-72.000;-82.000;-75.500;nan;nan;nan;nan;nan;-80.000;-52.000;-74.000;-64.000;-71.500;-77.000;nan;-76.000

+ 1
- 0
docker-compose.yml 查看文件

@@ -3,6 +3,7 @@ services:
build: . build: .
image: ble-ai-localizer:0.1.0 image: ble-ai-localizer:0.1.0
environment: environment:
PYTHONWARNINGS: "ignore:Unverified HTTPS request"
CONFIG: "/config/config.yaml" # Nome usato da main.py CONFIG: "/config/config.yaml" # Nome usato da main.py
SECRETS_FILE: "/config/secrets.yaml" SECRETS_FILE: "/config/secrets.yaml"
UI_USER: "Admin" UI_USER: "Admin"


+ 12
- 1
requirements.txt 查看文件

@@ -1,14 +1,25 @@
pandas==2.2.2
# --- CORE AI & DATA ---
numpy==1.26.4 numpy==1.26.4
scikit-learn==1.5.1 scikit-learn==1.5.1
joblib==1.4.2 joblib==1.4.2
pandas==2.2.2
pyarrow==15.0.0


# --- COMUNICAZIONE & SISTEMA ---
paho-mqtt==2.1.0 paho-mqtt==2.1.0
requests==2.32.3 requests==2.32.3
PyYAML==6.0.2 PyYAML==6.0.2
psutil==5.9.8
pathlib==1.0.1


# --- WEB & MAPPE ---
streamlit==1.49.1
pillow==10.2.0
fastapi==0.115.0 fastapi==0.115.0
uvicorn[standard]==0.30.6 uvicorn[standard]==0.30.6
folium==0.15.1
streamlit-folium==0.26.1


# --- VISUALIZZAZIONE ---
matplotlib==3.9.2 matplotlib==3.9.2
seaborn==0.13.2 seaborn==0.13.2

+ 13461
- 8687
tmp/main_process.log
文件差異過大導致無法顯示
查看文件


Loading…
取消
儲存