add ibeacon scan code to c3nav.js and endpoint

This commit is contained in:
Laura Klünder 2024-03-31 15:26:44 +02:00
parent a62017f3a2
commit 31c7dad72f
5 changed files with 82 additions and 26 deletions

View file

@ -25,7 +25,7 @@ from c3nav.mapdata.models import GraphEdge, LocationGroup
from c3nav.mapdata.models.access import AccessPermission
from c3nav.mapdata.models.geometry.space import ObstacleGroup
from c3nav.mapdata.models.theme import ThemeLocationGroupBackgroundColor, ThemeObstacleGroupBackgroundColor
from c3nav.routing.schemas import LocateRequestPeerSchema
from c3nav.routing.schemas import LocateRequestWifiPeerSchema
class EditorFormBase(I18nModelFormMixin, ModelForm):
@ -340,7 +340,7 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
item['rssi'] = item['level']
del item['level']
try:
LocateRequestPeerSchema.model_validate(item)
LocateRequestWifiPeerSchema.model_validate(item)
except PydanticValidationError as e:
raise ValidationError(str(e))
scan_data.append(item)

View file

@ -11,14 +11,17 @@ from c3nav.mapdata.models.access import AccessPermission
from c3nav.mapdata.schemas.models import CustomLocationSchema
from c3nav.mapdata.utils.cache.stats import increment_cache_key
from c3nav.routing.locator import Locator
from c3nav.routing.schemas import BSSIDSchema, LocateRequestPeerSchema
from c3nav.routing.schemas import BSSIDSchema, LocateRequestWifiPeerSchema, LocateRequestIBeaconPeerSchema
positioning_api_router = APIRouter(tags=["positioning"])
class LocateRequestSchema(BaseSchema):
peers: list[LocateRequestPeerSchema] = APIField(
title="list of visible/measured location beacons",
wifi_peers: list[LocateRequestWifiPeerSchema] = APIField(
title="list of visible/measured wifi location beacons",
)
ibeacon_peers: list[LocateRequestIBeaconPeerSchema] = APIField(
title="list of visible/measured location iBeacons",
)

View file

@ -12,7 +12,7 @@ from c3nav.mapdata.models import MapUpdate, Space
from c3nav.mapdata.models.geometry.space import RangingBeacon
from c3nav.mapdata.utils.locations import CustomLocation
from c3nav.routing.router import Router
from c3nav.routing.schemas import LocateRequestPeerSchema
from c3nav.routing.schemas import LocateRequestWifiPeerSchema
try:
from asgiref.local import Local as LocalContext
@ -146,7 +146,7 @@ class Locator:
cls.cached.data = cls.load_nocache(update)
return cls.cached.data
def convert_raw_scan_data(self, raw_scan_data: list[LocateRequestPeerSchema]) -> ScanData:
def convert_raw_scan_data(self, raw_scan_data: list[LocateRequestWifiPeerSchema]) -> ScanData:
return self.convert_scan(raw_scan_data, create_peers=False)
def get_xyz(self, address: BSSID) -> tuple[int, int, int] | None:
@ -160,7 +160,7 @@ class Locator:
peer: peer.xyz for peer in self.peers[:len(self.xyz)]
}
def locate(self, raw_scan_data: list[LocateRequestPeerSchema], permissions=None):
def locate(self, raw_scan_data: list[LocateRequestWifiPeerSchema], permissions=None):
scan_data = self.convert_raw_scan_data(raw_scan_data)
if not scan_data:
return None

View file

@ -1,7 +1,10 @@
from typing import Annotated, Union
from uuid import UUID
from annotated_types import Lt
from pydantic import Field as APIField
from pydantic import NegativeInt, PositiveInt
from pydantic.types import NonNegativeInt, PositiveFloat, NonNegativeFloat
from c3nav.api.schema import BaseSchema
from c3nav.api.utils import NonEmptyStr
@ -9,7 +12,7 @@ from c3nav.api.utils import NonEmptyStr
BSSIDSchema = Annotated[str, APIField(pattern=r"^[a-z0-9]{2}(:[a-z0-9]{2}){5}$", title="BSSID")]
class LocateRequestPeerSchema(BaseSchema):
class LocateRequestWifiPeerSchema(BaseSchema):
bssid: BSSIDSchema = APIField(
title="BSSID",
description="BSSID of the peer",
@ -61,3 +64,23 @@ class LocateRequestPeerSchema(BaseSchema):
description="standard deviation of measurements in meters",
example=1.23
)
class LocateRequestIBeaconPeerSchema(BaseSchema):
uuid: UUID = APIField(
title="UUID",
description="UUID of the iBeacon",
example="a142621a-2f42-09b3-245b-e1ac6356e9b0",
)
major: Annotated[NonNegativeInt, Lt(2 ** 16)] = APIField(
title="major value of the iBeacon",
)
minor: Annotated[NonNegativeInt, Lt(2 ** 16)] = APIField(
title="minor value of the iBeacon",
)
distance: NonNegativeFloat = APIField(
title="determined iBeacon distance",
)
last_seen_ago: NonNegativeInt = APIField(
title="how many milliseconds ago this beacon was last seen"
)

View file

@ -258,6 +258,7 @@ c3nav = {
if (window.mobileclient) {
c3nav.startWifiScanning();
c3nav.startBLEScanning();
}
c3nav.init_completed = true;
@ -1830,19 +1831,19 @@ c3nav = {
}
},
_no_wifi_count: 0,
startBLEScanning: function() {
if (mobileclient.registerBeaconUuid) {
mobileclient.registerBeaconUuid("a142621a-2f42-09b3-245b-e1ac6356e9b0");
}
},
_last_scan: 0,
_last_wifi_peers: [],
_last_ibeacon_peers: [],
_no_scan_count: 0,
_wifi_scan_results: function(peers) {
peers = JSON.parse(peers);
if (peers.length) {
c3nav._hasLocationPermission = true;
} else {
c3nav.hasLocationPermission(true);
}
var now = Date.now();
if (now-4000 < c3nav._last_wifi_scan) return;
if (c3nav.ssids) {
peers = peers.filter(peer => c3nav.ssids.includes(peer.ssid));
}
@ -1857,28 +1858,53 @@ c3nav = {
delete peer.rtt;
}
}
c3nav._last_wifi_peers = peers;
c3nav._after_scan_results();
},
_ibeacon_scan_results: function(peers) {
peers = JSON.parse(peers);
c3nav._last_ibeacon_peers = peers;
c3nav._after_scan_results();
},
_after_scan_results: function() {
has_peers = c3nav._last_wifi_peers.length || c3nav._last_ibeacon_peers.length;
if (has_peers) {
c3nav._hasLocationPermission = true;
} else {
c3nav.hasLocationPermission(true);
}
var now = Date.now();
if (now-4000 < c3nav._last_scan) return;
if (!peers.length) {
if (!has_peers) {
if (!c3nav._hasLocationPermission) {
c3nav._set_user_location(null);
} else {
if (c3nav._no_wifi_count > 5) {
c3nav._no_wifi_count = 0;
if (c3nav._no_scan_count > 5) {
c3nav._no_scan_count = 0;
c3nav._set_user_location(null);
} else {
c3nav._no_wifi_count++;
c3nav._no_scan_count++;
}
}
return;
}
c3nav._no_wifi_count = 0;
c3nav._no_scan_count = 0;
c3nav_api.post('positioning/locate/', {peers})
let ibeacon_peers = c3nav._last_ibeacon_peers.map(p => ({...p}));
for (let peer of ibeacon_peers) {
peer.last_seen_ago = Math.max(0, now - peer.last_seen);
}
c3nav_api.post('positioning/locate/', {
wifi_peers: c3nav._last_wifi_peers,
ibeacon_peers: ibeacon_peers,
})
.then(data => c3nav._set_user_location(data.location))
.catch(() => {
c3nav._set_user_location(null);
c3nav._last_wifi_scan = Date.now() + 20000
c3nav._last_scan = Date.now() + 20000
});
},
_current_user_location: null,
@ -2027,6 +2053,10 @@ function nearby_stations_available() {
c3nav._wifi_scan_results(mobileclient.getNearbyStations());
}
function ibeacon_results_available() {
c3nav._ibeacon_scan_results(mobileclient.getNearbyBeacons());
}
function openInModal(location) {
c3nav.open_modal();
$.get(location, c3nav._modal_loaded).fail(c3nav._modal_error);