1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
/// Libre Geolocation - server datastore
// Copyright 2024 Federico Ceratto <federico.ceratto@posteo.com>
// Released under AGPL
//
use anyhow::Result;
use bincode::{deserialize, serialize};
use log::info;
use serde::{Deserialize, Serialize};
// use sled::Db;
use std::fs;
use std::path::Path;
use libreloc_shared::*;
// pub struct SledDatastore {
// incoming: Db,
// wifi_bt: Db,
// wifi_bt_maps: Db,
// cells: Db,
// }
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct WiFiNode {
pub location: String,
pub fullhash: String, // secret
}
impl WiFiNode {
fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
serialize(&self)
}
fn from_bytes(bytes: &[u8]) -> Result<Self, bincode::Error> {
deserialize(bytes)
}
}
// impl SledDatastore {
// pub fn delete_all_data(&self) {
// self.wifi_bt.clear();
// self.cells.clear();
// self.incoming.clear();
// }
// // pub fn get_incoming(&self, key: String) -> Option<WiFiNode> {
// // if let Ok(Some(data)) = self.incoming.get(key) {
// // if let Ok(my_data) = deserialize::<WiFiNode>(&data) {
// // return Some(my_data);
// // }
// // }
// // None
// // }
// // pub fn set_incoming(&self, key: &[u8], my_data: &WiFiNode) {
// // let bytes = serialize(my_data).unwrap();
// // self.incoming.insert(key, bytes).unwrap();
// // }
// // WiFi: get
// pub fn get_wifi(&self, key: &str) -> Option<WiFiNode> {
// if let Ok(Some(data)) = self.wifi_bt.get(key) {
// if let Ok(my_data) = deserialize::<WiFiNode>(&data) {
// return Some(my_data);
// }
// }
// None
// }
// // WiFi: set
// pub fn set_wifi(&self, key: &str, my_data: &WiFiNode) {
// let bytes = serialize(my_data).unwrap();
// self.wifi_bt.insert(key, bytes).unwrap();
// }
// }
// pub fn open_sled(datadir: &str) -> Result<SledDatastore> {
// let path = Path::new(datadir); //.join("sled");
// fs::create_dir_all(path)?;
// info!("Opening {:?}", path.join("s1"));
// let incoming = sled::open(path.join("incoming"))?;
// let wifi_bt = sled::open(path.join("wifi_bt"))?;
// let wifi_bt_maps = sled::open(path.join("wifi_bt_maps"))?;
// let cells = sled::open(path.join("cells"))?;
// Ok(SledDatastore {
// incoming,
// wifi_bt,
// wifi_bt_maps,
// cells,
// })
// }
// In-memory
use std::collections::BTreeMap;
use std::collections::{HashMap, HashSet};
// pub type FP0 = [u8; 2];
// pub type Lok0 = [u8; 2];
// pub type MiniMap0 = HashMap<Lok0, u16>;
// pub type FP1 = [u8; 2]; // len of Lok0 + FP0
// pub type Lok1 = [u8; 2];
// pub type MiniMap1 = HashMap<Lok1, u16>;
pub struct MemDatastore {
// pub wifi_bt: BTreeMap<String, WiFiNode>,
wifi_bt_map: BTreeMap<String, u8>,
pub wifi_bt_bh: BloomHashStore,
// pub wifi_bt_map0: HashMap<FP0, HashSet<String>>,
}
// Type inference lets us omit an explicit type signature (which
// would be `BTreeMap<String, String>` in this example).
pub fn open_mem_datastore() -> MemDatastore {
MemDatastore {
// wifi_bt: BTreeMap::new(),
wifi_bt_map: BTreeMap::new(),
wifi_bt_bh: BloomHashStore::new(),
// wifi_bt_map0: HashMap::new(),
}
}
impl MemDatastore {
pub fn insert_blip(&mut self, blip: &Blip) {
// TODO
}
/// Insert "raw" datapoint (lat, lon, macaddr...)
pub fn insert_datapoint(&mut self, lat: f64, lon: f64, mac: &Mac, ssid: &str) {
//
let c = geohash::Coord { x: lon, y: lat };
let geoh = coord_to_geohash(c);
for s in GEOHASH_DEPTHS {
// The same blip generates multiple fingerprints that as needed by clients
// doing location at different geohash-depths
let geopath = &geoh[..s];
let fp = generate_fingerprint_wifi(&geopath, mac, ssid);
let b = geoh[s..s + 2].as_bytes();
let geosegment: GeoSegment = [b[0], b[1]];
self.wifi_bt_bh.insert(&fp, &geosegment)
}
}
pub fn increment_wifi_map(&mut self, k: &str) {
// *self.wifi_bt_map.entry(k.to_string()).or_insert(0) += 1;
self.wifi_bt_map.insert(k.to_owned(), 1);
}
/// Extract a minimap representing a geographical map of all devices
/// matching a fingerprint
pub fn scan_minimap(&self, prefix: &str, skip: usize) -> MiniMap {
let start = prefix.to_string();
let mut end = prefix.to_string();
end.push(char::MAX);
assert!(
matches!(prefix.len(), 9 | 6 | 3 | 7 | 4),
"unexpected len {}",
prefix
);
self.wifi_bt_map
.range(start..end)
.map(|(k, v)| (k.chars().skip(skip).collect(), *v))
.collect()
}
}