Skip to content

Commit 7d6d7d0

Browse files
committed
put-sample to D1
1 parent 491333e commit 7d6d7d0

4 files changed

Lines changed: 67 additions & 44 deletions

File tree

addSample.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
<label for="path">Path:</label>
1717
<input type="text" id="path" name="path">
1818
</div>
19+
<div class="form-item">
20+
<label for="path">SNR:</label>
21+
<input type="text" id="snr" name="snr">
22+
</div>
23+
<div class="form-item">
24+
<label for="path">RSSI:</label>
25+
<input type="text" id="rssi" name="rssi">
26+
</div>
1927
<div class="form-item">
2028
<label for="path">Observed:</label>
2129
<input type="checkbox" id="observed" name="observed">
@@ -32,6 +40,8 @@
3240
const $ = (id) => document.getElementById(id);
3341
const locTxt = $('loc');
3442
const pathTxt = $('path');
43+
const snrTxt = $('snr');
44+
const rssiTxt = $('rssi');
3545
const observedCB = $('observed');
3646
const statusDiv = $('status');
3747

@@ -46,16 +56,23 @@
4656
function clearForm() {
4757
locTxt.value = '';
4858
pathTxt.value = '';
59+
snrTxt.value = '';
60+
rssiTxt.value = '';
61+
observedCB.checked = false;
4962
}
5063

5164
async function putSample() {
5265
const [latVal, lonVal] = locTxt.value.split(/\s*,?\s+/)
5366
const [lat, lon] = parseLocation(latVal, lonVal);
5467
const pathVal = pathTxt.value;
68+
const snrVal = snrTxt.value;
69+
const rssiVal = rssiTxt.value;
5570
const payload = {
5671
lat: lat,
5772
lon: lon,
5873
path: pathVal !== "" ? pathVal.split(',') : [],
74+
snr: snrVal !== "" ? Number(snrVal) : null,
75+
rssi: rssiVal !== "" ? Number(rssiVal) : null,
5976
observed: observedCB.checked
6077
};
6178

functions/put-sample.js

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,49 @@
11
import {
2-
ageInDays,
3-
definedOr,
4-
or,
52
parseLocation,
6-
retry,
73
sampleKey
84
} from '../content/shared.js'
95

10-
function mergeMetadata(a, b) {
11-
const merged = {
12-
time: Math.max(a.time, b.time),
13-
snr: definedOr(Math.max, a.snr, b.snr),
14-
rssi: definedOr(Math.max, a.rssi, b.rssi),
15-
observed: definedOr(or, a.observed, b.observed),
16-
};
17-
18-
const setA = new Set(a.path);
19-
const setB = new Set(b.path);
20-
merged.path = Array.from(setA.union(setB));
21-
22-
return merged;
23-
}
24-
256
export async function onRequest(context) {
267
const request = context.request;
278
const data = await request.json();
28-
const store = context.env.SAMPLES;
299

10+
// TODO: Pass geohash directly.
3011
const [lat, lon] = parseLocation(data.lat, data.lon);
3112
const key = sampleKey(lat, lon);
32-
const path = (data.path ?? []).map(p => p.toLowerCase());
33-
let metadata = {
34-
time: Date.now(),
35-
rssi: data.rssi ?? null,
36-
snr: data.snr ?? null,
37-
path: path,
38-
observed: data.observed ?? false,
39-
};
40-
41-
// KV only allows one write to a key per second.
42-
// There's a strong possibility that's hit by #wardrive.
43-
await retry(async () => {
44-
const resp = await store.getWithMetadata(key);
45-
if (resp.value !== null
46-
&& resp.metadata !== null
47-
&& ageInDays(resp.metadata.time) < 1) {
48-
// Merge new information with existing if recent.
49-
metadata = mergeMetadata(metadata, resp.metadata);
50-
}
5113

52-
console.log(`PUT ${key} -> ${JSON.stringify(metadata)}`);
53-
await store.put(key, "", {
54-
metadata: metadata
55-
});
56-
});
14+
const time = Date.now();
15+
const rssi = data.rssi ?? null;
16+
const snr = data.snr ?? null;
17+
const path = (data.path ?? []).map(p => p.toLowerCase());
18+
const observed = data.observed ?? false;
19+
20+
await context.env.DB
21+
.prepare(`
22+
INSERT INTO samples (hash, time, rssi, snr, observed, repeaters)
23+
VALUES (?, ?, ?, ?, ?, ?)
24+
ON CONFLICT(hash) DO UPDATE SET
25+
time = excluded.time,
26+
rssi = CASE
27+
WHEN samples.rssi IS NULL THEN excluded.rssi
28+
WHEN excluded.rssi IS NULL THEN samples.rssi
29+
ELSE MAX(samples.rssi, excluded.rssi)
30+
END,
31+
snr = CASE
32+
WHEN samples.snr IS NULL THEN excluded.snr
33+
WHEN excluded.snr IS NULL THEN samples.snr
34+
ELSE MAX(samples.snr, excluded.snr)
35+
END,
36+
observed = MAX(samples.observed, excluded.observed),
37+
repeaters = (
38+
SELECT json_group_array(value) FROM (
39+
SELECT value FROM json_each(samples.repeaters)
40+
UNION
41+
SELECT value FROM json_each(excluded.repeaters)
42+
)
43+
)
44+
`)
45+
.bind(key, time, rssi, snr, observed, JSON.stringify(path))
46+
.run();
5747

5848
return new Response('OK');
5949
}

support/schema.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
DROP TABLE IF EXISTS samples;
2+
CREATE TABLE IF NOT EXISTS samples (
3+
hash TEXT PRIMARY KEY,
4+
time INTEGER NOT NULL,
5+
rssi REAL CHECK (rssi IS NULL OR typeof(rssi) = 'real'),
6+
snr REAL CHECK (snr IS NULL OR typeof(snr) = 'real'),
7+
observed INTEGER NOT NULL DEFAULT 0 CHECK (observed IN (0, 1)),
8+
repeaters TEXT NOT NULL DEFAULT '[]'
9+
);

wrangler.jsonc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
"compatibility_flags": [
77
"nodejs_compat"
88
],
9+
"d1_databases": [
10+
{
11+
"binding": "DB",
12+
"database_name": "mesh-map",
13+
"database_id": "60046b37-9e23-4af3-879d-232d4663032d"
14+
}
15+
],
916
"kv_namespaces": [
1017
{
1118
"binding": "SAMPLES",

0 commit comments

Comments
 (0)