pond
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
}
|
||||
|
||||
.download-speed-limits-button + .download-speed-limits-button {
|
||||
margin-top: var(--padding-sm);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.download-speed-limits-button:hover {
|
||||
@@ -22,25 +22,39 @@
|
||||
transform: var(--hover-scale-sm);
|
||||
}
|
||||
.download-speed-limits-button-wrapper {
|
||||
height: calc(100% + 20px);
|
||||
position: relative;
|
||||
align-items: stretch;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding-sm);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.download-speed-limits-link {
|
||||
color: var(--text-color);
|
||||
font-size: var(--font-size-sm);
|
||||
left: 50%;
|
||||
margin-top: var(--border-radius-sm);
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
top: 100%;
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.download-speed-limits-note {
|
||||
color: var(--secondary-fg);
|
||||
font-size: var(--font-size-sm);
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.download-speed-limits-status {
|
||||
color: var(--text-color);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-demi-bold);
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.download-speed-limits-text {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,58 @@
|
||||
import { html } from "/assets/vendor/arrow-core.js"
|
||||
import { html, reactive } from "/assets/vendor/arrow-core.js"
|
||||
|
||||
const state = reactive({
|
||||
canProcessNow: false,
|
||||
fetched: false,
|
||||
loading: true,
|
||||
processing: false,
|
||||
reason: "",
|
||||
status: "Checking...",
|
||||
submitting: false,
|
||||
})
|
||||
|
||||
let pollTimer = null
|
||||
|
||||
async function fetchStatus() {
|
||||
try {
|
||||
const response = await fetch("/api/speed_limits/status")
|
||||
const result = await response.json()
|
||||
|
||||
state.canProcessNow = Boolean(result.canProcessNow)
|
||||
state.processing = Boolean(result.processing)
|
||||
state.reason = result.reason || ""
|
||||
state.status = result.status || "Idle"
|
||||
} catch (error) {
|
||||
state.canProcessNow = false
|
||||
state.processing = false
|
||||
state.reason = "Failed to load processor status."
|
||||
state.status = "Unavailable"
|
||||
}
|
||||
|
||||
state.loading = false
|
||||
}
|
||||
|
||||
async function handleProcessNow() {
|
||||
if (state.submitting || state.processing || !state.canProcessNow) {
|
||||
return
|
||||
}
|
||||
|
||||
state.submitting = true
|
||||
try {
|
||||
const response = await fetch("/api/speed_limits/process", { method: "POST" })
|
||||
const result = await response.json()
|
||||
|
||||
if (response.ok) {
|
||||
showSnackbar(result.message || "Speed limit processing started.")
|
||||
} else {
|
||||
showSnackbar(result.error || "Failed to start speed limit processing.", "error")
|
||||
}
|
||||
} catch (error) {
|
||||
showSnackbar("Failed to start speed limit processing.", "error")
|
||||
}
|
||||
|
||||
state.submitting = false
|
||||
fetchStatus()
|
||||
}
|
||||
|
||||
export function SpeedLimits() {
|
||||
function handleDownload() {
|
||||
@@ -10,16 +64,38 @@ export function SpeedLimits() {
|
||||
showSnackbar("Download started...")
|
||||
}
|
||||
|
||||
if (!state.fetched) {
|
||||
state.fetched = true
|
||||
fetchStatus()
|
||||
|
||||
if (pollTimer === null) {
|
||||
pollTimer = setInterval(fetchStatus, 3000)
|
||||
}
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="download-speed-limits-wrapper">
|
||||
<section class="download-speed-limits-widget">
|
||||
<div class="download-speed-limits-title">Download Speed Limits</div>
|
||||
<p class="download-speed-limits-text">
|
||||
Download speed limit data collected using "Speed Limit Filler".
|
||||
Enable "Speed Limit Filler" on the device, drive to collect data, then process and download it here when parked.
|
||||
</p>
|
||||
<p class="download-speed-limits-status">
|
||||
${() => state.loading ? "Checking processor status..." : `Processor Status: ${state.status}`}
|
||||
</p>
|
||||
${() => !state.loading && state.reason && state.reason !== state.status ? html`
|
||||
<p class="download-speed-limits-note">${state.reason}</p>
|
||||
` : ""}
|
||||
<div class="download-speed-limits-button-wrapper">
|
||||
<button class="download-speed-limits-button" @click="${handleDownload}">Download</button>
|
||||
<a class="download-speed-limits-link" href="https://SpeedLimitFiller.starpilot.download" target="_blank">
|
||||
<button
|
||||
class="download-speed-limits-button"
|
||||
@click="${handleProcessNow}"
|
||||
disabled="${() => state.submitting || state.processing || !state.canProcessNow}"
|
||||
>
|
||||
${() => state.processing || state.submitting ? "Processing..." : "Process Now"}
|
||||
</button>
|
||||
<a class="download-speed-limits-link" href="https://nerf.077769.xyz/" target="_blank" rel="noopener noreferrer">
|
||||
Submit speed limits here
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -3839,6 +3839,78 @@ def setup(app):
|
||||
buffer.seek(0)
|
||||
return send_file(buffer, as_attachment=True, download_name="speed_limits.json", mimetype="application/json")
|
||||
|
||||
def _speed_limits_status_payload():
|
||||
status = params_memory.get("UpdateSpeedLimitsStatus", encoding="utf-8") or ""
|
||||
processing = bool(status and status != "Completed!")
|
||||
enabled = params.get_bool("SpeedLimitFiller")
|
||||
is_onroad = params.get_bool("IsOnroad")
|
||||
time_valid = system_time_valid()
|
||||
|
||||
network_connected = True
|
||||
try:
|
||||
sm = messaging.SubMaster(["deviceState"], poll="deviceState")
|
||||
sm.update(0)
|
||||
network_connected = sm["deviceState"].networkType != log.DeviceState.NetworkType.none
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
overpass_requests = {}
|
||||
try:
|
||||
overpass_requests = json.loads(params.get("OverpassRequests", encoding="utf-8") or "{}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
current_day = datetime.now(timezone.utc).day
|
||||
saved_day = int(overpass_requests.get("day", current_day) or current_day)
|
||||
total_requests = int(overpass_requests.get("total_requests", 0) or 0)
|
||||
max_requests = int(overpass_requests.get("max_requests", 10000) or 10000)
|
||||
if saved_day != current_day:
|
||||
total_requests = 0
|
||||
api_limit_hit = total_requests >= max_requests
|
||||
|
||||
reason = ""
|
||||
if not enabled:
|
||||
reason = "Enable Speed Limit Filler on the device first."
|
||||
elif processing:
|
||||
reason = status
|
||||
elif is_onroad:
|
||||
reason = "Processing is only available while parked."
|
||||
elif not time_valid:
|
||||
reason = "System time is not valid yet."
|
||||
elif not network_connected:
|
||||
reason = "Connect the device to the internet first."
|
||||
elif api_limit_hit:
|
||||
reason = "Today's Overpass API request limit has been reached."
|
||||
|
||||
return {
|
||||
"apiLimitHit": api_limit_hit,
|
||||
"canProcessNow": enabled and not processing and not is_onroad and time_valid and network_connected and not api_limit_hit,
|
||||
"enabled": enabled,
|
||||
"isOnroad": is_onroad,
|
||||
"networkConnected": network_connected,
|
||||
"processing": processing,
|
||||
"reason": reason,
|
||||
"status": status or "Idle",
|
||||
"timeValid": time_valid,
|
||||
"totalRequests": total_requests,
|
||||
"maxRequests": max_requests,
|
||||
}
|
||||
|
||||
@app.route("/api/speed_limits/status", methods=["GET"])
|
||||
def speed_limits_status():
|
||||
return jsonify(_speed_limits_status_payload()), 200
|
||||
|
||||
@app.route("/api/speed_limits/process", methods=["POST"])
|
||||
def process_speed_limits():
|
||||
payload = _speed_limits_status_payload()
|
||||
if not payload["canProcessNow"]:
|
||||
return jsonify({"error": payload["reason"] or "Speed limit processing is unavailable right now."}), 409
|
||||
|
||||
params_memory.put("UpdateSpeedLimitsStatus", "Calculating...")
|
||||
params_memory.put_bool("UpdateSpeedLimits", True)
|
||||
|
||||
return jsonify({"message": "Speed limit processing started.", "status": "Calculating..."}), 202
|
||||
|
||||
@app.route("/api/stats", methods=["GET"])
|
||||
def get_stats():
|
||||
build_metadata = get_build_metadata()
|
||||
|
||||
Reference in New Issue
Block a user