1A12 / app.py
SalexAI's picture
Update app.py
658b428 verified
raw
history blame
5.12 kB
# Sir, this app.py implements /stream and /dashboard endpoints with telemetry streaming, offline buffering, and CSV download.
# Sir, let me know if you'd like a detailed walkthrough.
from fastapi import FastAPI, WebSocket, Request
from fastapi.responses import HTMLResponse, StreamingResponse
import uvicorn
import json
import csv
from io import StringIO
app = FastAPI()
telemetry_data = [] # Store telemetry dicts
# Active WebSocket connections for dashboard clients
dashboard_connections = []
@app.get("/stream")
async def stream():
html = """
<!DOCTYPE html>
<html>
<head><title>Emerson Telemetry Stream</title></head>
<body>
<h1>Emerson Telemetry Stream</h1>
<p id="status">Connecting...</p>
<video id="video" width="320" height="240" autoplay muted></video>
<script>
const ws = new WebSocket(`ws://${location.host}/ws`);
let buffer = [];
document.getElementById("status").innerText = navigator.onLine ? "Connected" : "Waiting for Emerson...";
window.addEventListener("online", () => flushBuffer());
window.addEventListener("offline", () => document.getElementById("status").innerText = "Waiting for Emerson...");
ws.onopen = () => { document.getElementById("status").innerText = "Connected"; flushBuffer(); };
ws.onclose = () => document.getElementById("status").innerText = "Disconnected";
ws.onerror = () => document.getElementById("status").innerText = "Error";
async function sendTelemetry() {
const canvas = document.createElement("canvas");
const video = document.getElementById("video");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext("2d");
ctx.drawImage(video, 0, 0);
const image = canvas.toDataURL("image/jpeg");
navigator.geolocation.getCurrentPosition(pos => {
const data = { timestamp: Date.now(), gps: pos.coords, image };
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
} else {
buffer.push(data);
}
});
}
function flushBuffer() {
while (buffer.length) {
ws.send(JSON.stringify(buffer.shift()));
}
}
navigator.mediaDevices.enumerateDevices().then(devices => {
const videoInputs = devices.filter(d => d.kind === "videoinput");
if (videoInputs.length) {
navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: videoInputs[0].deviceId } } })
.then(stream => { document.getElementById("video").srcObject = stream; });
}
});
setInterval(sendTelemetry, 1000);
</script>
</body>
</html>
"""
return HTMLResponse(html)
@app.get("/dashboard")
async def dashboard():
html = """
<!DOCTYPE html>
<html>
<head><title>Emerson Dashboard</title></head>
<body>
<h1>Emerson Dashboard</h1>
<p id="status">Waiting for Emerson...</p>
<div id="data"></div>
<a href="/download-csv" download="telemetry.csv">Download CSV</a>
<script>
const ws = new WebSocket(`ws://${location.host}/ws`);
ws.onopen = () => document.getElementById("status").innerText = "Connected";
ws.onmessage = msg => {
const data = JSON.parse(msg.data);
document.getElementById("status").innerText = "Streaming";
const div = document.getElementById("data");
const p = document.createElement("p");
p.innerText = `Time: ${new Date(data.timestamp).toLocaleTimeString()} GPS: ${data.gps.latitude.toFixed(5)}, ${data.gps.longitude.toFixed(5)}`;
const img = document.createElement("img");
img.src = data.image;
img.width = 160;
div.prepend(img);
div.prepend(p);
};
ws.onclose = () => document.getElementById("status").innerText = "Disconnected";
</script>
</body>
</html>
"""
return HTMLResponse(html)
@app.get("/download-csv")
async def download_csv():
def iter_csv():
si = StringIO()
writer = csv.writer(si)
writer.writerow(["timestamp", "latitude", "longitude", "image"])
for d in telemetry_data:
writer.writerow([d["timestamp"], d["gps"]["latitude"], d["gps"]["longitude"], d["image"]])
yield si.getvalue()
si.seek(0)
si.truncate(0)
return StreamingResponse(iter_csv(), media_type="text/csv")
@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
await ws.accept()
dashboard_connections.append(ws)
try:
while True:
data = await ws.receive_text()
telemetry = json.loads(data)
telemetry_data.append(telemetry)
# Broadcast to dashboard clients
for conn in dashboard_connections:
try:
await conn.send_text(json.dumps(telemetry))
except:
dashboard_connections.remove(conn)
except:
dashboard_connections.remove(ws)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)