File size: 5,116 Bytes
658b428
 
c627618
658b428
 
 
 
 
 
c627618
658b428
 
 
 
c627618
658b428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c627618
658b428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c627618
658b428
 
 
 
 
c627618
658b428
 
 
 
 
 
 
c627618
658b428
 
 
 
 
 
c627618
658b428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c627618
658b428
 
 
 
 
 
 
 
 
 
 
 
c627618
658b428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c627618
 
658b428
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
# 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)