Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added web/client/public/favicon.ico
Binary file not shown.
27 changes: 24 additions & 3 deletions web/client/public/index.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
@import url("https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&display=swap");

* {
margin: 0;
padding: 0;
cursor: default;
font-family: "Geist Mono";
}

body,
Expand All @@ -11,9 +14,14 @@ html {

.flex {
min-height: 100%;
max-height: 100%;
display: flex;
}

.flex-1 {
flex: 1;
}

.flex-row {
flex-direction: row;
}
Expand All @@ -22,20 +30,33 @@ html {
flex-direction: column;
}

.navbar {
padding: 4px;
overflow-x: auto;
min-height: 30px;
}

.navbar-item {
padding: 4px;
white-space: nowrap;
}

#map {
flex: 1;
max-height: 50%;
}

#sensor-table {
flex: 0;
}

.charts {
max-width: 100%;
max-width: 520px;
max-height: 100%;
overflow: auto;
position: relative;
}

.chart {
.chart-container {
position: relative;
flex: 1;
}
29 changes: 20 additions & 9 deletions web/client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,31 @@
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""
></script>
<title>xd2</title>
<title>Cosmo PK rocket status preview</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/index.css" />
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="flex flex-col">
<div id="map"></div>
<div class="flex flex-row charts">
<div class="chart"><canvas class="chart" id="temp-chart"></canvas></div>
<div class="chart">
<canvas class="chart" id="pressure-chart"></canvas>
</div>
<div class="chart">
<canvas class="chart" id="altitude-chart"></canvas>
<div class="navbar flex flex-row no-wrap">
<div class="navbar-item flex-1" id="status">Loading...</div>
<div class="navbar-item flex-1" id="latitude"></div>
<div class="navbar-item flex-1" id="longitude"></div>
<div class="navbar-item flex-1" id="time"></div>
</div>
<div class="flex flex-row flex-1">
<div id="map"></div>
<div class="flex flex-col charts flex-1">
<div class="chart-container flex-1">
<canvas class="chart" id="temp-chart"></canvas>
</div>
<div class="chart-container flex-1">
<canvas class="chart" id="pressure-chart"></canvas>
</div>
<div class="chart-container flex-1">
<canvas class="chart" id="altitude-chart"></canvas>
</div>
</div>
</div>
</div>
Expand Down
109 changes: 83 additions & 26 deletions web/client/public/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,55 @@ export const db = getDatabase(app);

const dbRef = ref(db);

const entryLimit = 80;
Chart.defaults.font.family = "Geist Mono";

const entryLimit = 160;
const flyToCurrentPositionOnUpdate = false;

let tempChart = null;
let pressureChart = null;
let altitudeChart = null;

let statusText = document.querySelector("#status");
let latitudeText = document.querySelector("#latitude");
let longitudeText = document.querySelector("#longitude");
let timeText = document.querySelector("#time");

let data = {};
let lastN = 0;
const polylineData = [];
let polyline;

function isPresent(value) {
return value !== null && value !== undefined;
}

function cloneObject(object) {
return JSON.parse(JSON.stringify(object));
}

const RocketStatus = {
Dev: 0,
Idle: 1,
PreLaunch: 2,
Climbing: 3,
Apogee: 4,
DescentPrimary: 5,
Recovery: 6,
Error: 99,
};

const rocketStatusToText = {
[RocketStatus.Dev]: "dev",
[RocketStatus.Idle]: "idle/testing",
[RocketStatus.PreLaunch]: "pre-launch",
[RocketStatus.Climbing]: "climbing",
[RocketStatus.Apogee]: "at apogee",
[RocketStatus.DescentPrimary]: "descending",
[RocketStatus.Recovery]: "landed",
[RocketStatus.Error]: "error",
};

const defaultData = {
bmp: {
altitude: 0,
Expand Down Expand Up @@ -88,9 +122,9 @@ const defaultData = {
status: 0,
};

function pushToArrayAndTrim(array, data) {
function pushToArrayAndTrim(array, data, limit = entryLimit) {
array.push(data);
if (array.length > entryLimit) {
if (array.length > limit) {
array.splice(0, 1);
}
}
Expand All @@ -112,6 +146,22 @@ function backfillComponent(incomingData, component, key) {
}
}

function pushChartData(chart, data, time, limit = entryLimit) {
pushToArrayAndTrim(
chart.data.labels,
formatDate(time.hours, time.minutes, time.seconds),
limit,
);
chart.data.datasets.forEach((dataset) => {
pushToArrayAndTrim(dataset.data, data, limit);
});
chart.update("none");
}

function formatDate(hours = 0, minutes = 0, seconds = 0) {
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
}

function setData(incomingData) {
try {
data = incomingData;
Expand Down Expand Up @@ -147,27 +197,17 @@ function setData(incomingData) {
pushChartData(altitudeChart, data.bmp.altitude, time);

pushPolylineData(data.gps.latitude, data.gps.longitude);

statusText.innerText = `Status: ${rocketStatusToText[data.status]}`;
latitudeText.innerText = `Latitude: ${data.gps.latitude}`;
longitudeText.innerText = `Longitude: ${data.gps.longitude}`;
timeText.innerText = `Time: ${formatDate(time.hours, time.minutes, time.seconds)}`;
} catch (err) {
data = cloneObject(defaultData);
console.log("error", err);
}
}

function pushChartData(chart, data, time) {
pushToArrayAndTrim(
chart.data.labels,
formatDate(time.hours, time.minutes, time.seconds),
);
chart.data.datasets.forEach((dataset) => {
pushToArrayAndTrim(dataset.data, data);
});
chart.update();
}

function formatDate(hours = 0, minutes = 0, seconds = 0) {
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
}

function initChart(handle, name, color) {
const ctx = document.querySelector(`#${handle}-chart`);
return new Chart(ctx, {
Expand All @@ -190,6 +230,12 @@ function initChart(handle, name, color) {
});
}

window.addEventListener("resize", () => {
tempChart.resize();
pressureChart.resize();
altitudeChart.resize();
});

function initCharts() {
tempChart = initChart("temp", "Temperature", { r: 79, g: 192, b: 192 });
pressureChart = initChart("pressure", "Pressure", { r: 192, g: 79, b: 192 });
Expand Down Expand Up @@ -237,7 +283,7 @@ async function init() {

{
const historicalSnapshots = await retrieveHistoricalSnapshots(loraRef);
const skipCount = 4; // snapshots are apparently taken 4 times a second
const skipCount = Math.floor(historicalSnapshots.length / entryLimit);
for (let i = 0; i < historicalSnapshots.length; i += skipCount) {
const snapshot = historicalSnapshots[i];
setData(snapshot);
Expand All @@ -249,18 +295,29 @@ async function init() {
marker.setLatLng(newLoc);
}

function updateApp(data) {
setData(data);
if (flyToCurrentPositionOnUpdate) {
map.flyTo([data.gps.latitude, data.gps.longitude], 14);
}
setMarkerLocation(data.gps.latitude, data.gps.longitude);
}

onValue(loraRef, (snapshot) => {
const latestData = Object.values(snapshot.val())[0];
const snapshots = Object.values(latestData);
const data = snapshots[snapshots.length - 1];
if (data.n) {
if (data.n == lastN) {
const latestDataEntry = snapshots[snapshots.length - 1];
if (latestDataEntry.n) {
if (latestDataEntry.n == lastN) {
return;
}
setData(data);
map.flyTo([data.gps.latitude, data.gps.longitude], 14);
setMarkerLocation(data.gps.latitude, data.gps.longitude);
lastN = data.n;
if (
isPresent(latestDataEntry.gps.longitude) &&
isPresent(latestDataEntry.gps.latitude)
) {
updateApp(latestDataEntry);
}
lastN = latestDataEntry.n;
}
});
}
Expand Down