Skip to content

Commit

Permalink
Revamp the entire data storage solution and fix conops connection pro…
Browse files Browse the repository at this point in the history
…blem
  • Loading branch information
maxslarsson committed Jan 30, 2025
1 parent f667cf7 commit 3ed93d0
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 308 deletions.
6 changes: 3 additions & 3 deletions ground-server/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ThemeProvider } from "@/components/theme-provider";
import Navbar from "@/components/navbar";

import { Toaster } from "@/components/ui/toaster"
import { WebSocketProvider } from "@/contexts/websocket-context";
import { DataProvider } from "@/contexts/data-context";

const geistSans = localFont({
src: "./fonts/GeistVF.woff",
Expand Down Expand Up @@ -44,11 +44,11 @@ export default function RootLayout({
disableTransitionOnChange
>
<SessionProvider>
<WebSocketProvider>
<DataProvider>
<Navbar />
<main>{children}</main>
<Toaster /> {/* Place Toaster here within the first return statement */}
</WebSocketProvider>
</DataProvider>
</SessionProvider>
</ThemeProvider>
</body>
Expand Down
179 changes: 21 additions & 158 deletions ground-server/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { DashboardWidget } from "@/components/dashboard/dashboard-widget";
import { TelemetryAdder } from "@/components/dashboard/telemetry-adder";
import { PresetSelector } from "@/components/dashboard/preset-selector";

import { useWebSocket } from "@/contexts/websocket-context";
import { type DataPoint, type TelemetryChannel } from "@/lib/definitions";
import { useData } from "@/contexts/data-context";
import { type TelemetryChannel } from "@/lib/definitions";

const ResponsiveReactGridLayout = WidthProvider(Responsive);

Expand All @@ -23,142 +23,42 @@ export default function Home() {
{
id: string;
channel: TelemetryChannel;
}[]
>([]);
const [layouts, setLayouts] = useState<
{
id: string;
layout: Layout;
}[]
>([]);
const [data, setData] = useState<
{
id: string;
data: DataPoint[];
}[]
>([]);
const ws = useWebSocket();

const { data, sendHistoricalDataReq } = useData();

// Load layouts and channels from localStorage on mount
useEffect(() => {
const storedLayouts = localStorage.getItem("layouts");
const storedChannels = localStorage.getItem("channels");

if (storedLayouts && storedChannels) {
const parsedLayouts = JSON.parse(storedLayouts);
setLayouts(parsedLayouts);

if (storedChannels) {
const parsedChannels = JSON.parse(storedChannels);
const channels = parsedChannels.map(
(channel: { id: string; channel_id: string }) => ({
(channel: { id: string; channel_id: string, layout: Layout }) => ({
id: channel.id,
channel: TELEMETRY_CHANNELS.find((c) => c.id === channel.channel_id)!,
layout: channel.layout
})
);
setChannels(channels);

// Initialize data for each channel
const initialData = parsedChannels.map((channel: { id: string }) => ({
id: channel.id,
data: [],
}));
setData(initialData);
}
}, []);

useEffect(() => {
if (ws.ws) {
ws.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log(message);

if (message.historical) {
// Historical data
console.log("Historical Message Received");

setData((prevData) =>
prevData.map((data) => {
const channel = channels.find(
(channel) => channel.id === data.id
)!.channel;

if (channel.dbField === message.field) {
console.log("Widget has matching field");

const widgetData: DataPoint[] = message.data.map(
(item: { timestamp: string; value: number }) => {
return {
timestamp: new Date(item.timestamp),
value: item.value,
};
}
);

return {
id: data.id,
data: widgetData,
};
} else {
console.log(
`Fields did not match ${channel.dbField} and ${message.field}`
);
return data;
}
})
);
} else {
// Live data

setData((prevData) =>
prevData.map((data) => {
const channel = channels.find(
(channel) => channel.id === data.id
)!.channel;

const field = channel.dbField;
let newValue = null;
if (field in message) {
console.log("%s in message.", field);
newValue = message[field];
} else {
console.log("%s not in message.", field);
}

if (newValue) {
return {
id: data.id,
data: [
...data.data,
{ timestamp: new Date(), value: newValue },
],
};
}

return data;
})
);
}
};
}
}, [ws.ws, channels]);

const onLayoutChange = (layout: Layout[]) => {
setLayouts((prevLayouts) => {
const newLayout = prevLayouts.map((prevLayout) => {
const updatedLayout = layout.find((l) => l.i === prevLayout.id);
setChannels((prevChannels) => {
const newLayout = prevChannels.map((prevChannel) => {
const updatedLayout = layout.find((l) => l.i === prevChannel.id);
if (updatedLayout) {
return {
id: prevLayout.id,
...prevChannel,
layout: updatedLayout,
};
}
return prevLayout;
return prevChannel;
});

// 1. Better error handling
// 2. Ties into telemetry right away
// 3. Store mode

localStorage.setItem("layouts", JSON.stringify(newLayout));
localStorage.setItem(
"channels",
Expand All @@ -167,6 +67,7 @@ export default function Home() {
return {
id: c.id,
channel_id: c.channel.id,
layout: c.layout,
};
})
)
Expand All @@ -180,68 +81,38 @@ export default function Home() {
const onWidgetModeChange = (widgetId: string, newMode: string) => {
const channel = channels.find((channel) => channel.id === widgetId)!;
if (channel.id === widgetId && newMode === "15m Chart") {
if (ws.ws) {
ws.ws.send(
JSON.stringify({
start: -15,
stop: 0,
field: channel.channel.dbField,
})
);
} else {
console.error(
"WebSocket not connected. Cannot request historical data."
);
}
sendHistoricalDataReq(-15, 0, channel.channel.dbField);
} else if (channel.id === widgetId && newMode === "60m Chart") {
if (ws.ws) {
ws.ws.send(
JSON.stringify({
start: -60,
stop: 0,
field: channel.channel.dbField,
})
);
} else {
console.error(
"WebSocket not connected. Cannot request historical data."
);
}
sendHistoricalDataReq(-60, 0, channel.channel.dbField);
}
};

return channels.map((channel) => {
const deleteWidget = () => {
console.log("Deleting widget", channel.id);
setChannels((prevChannels) =>
prevChannels.filter((c) => c.id !== channel.id)
);
setLayouts((prevLayouts) =>
prevLayouts.filter((w) => w.layout.i !== channel.id)
);
setData((prevData) => prevData.filter((d) => d.id !== channel.id));
setChannels((prevChannels) => prevChannels.filter((c) => c.id !== channel.id));
};

return (
<DashboardWidget
key={channel.id}
id={channel.id}
channel={channel.channel}
data={data.find((d) => d.id === channel.id)!.data}
data={data[channel.channel.dbField]}
deleteWidget={deleteWidget}
onModeChange={onWidgetModeChange}
/>
);
});
}, [ws.ws, channels, data]);
}, [channels, data]);

return (
<div>
<ResponsiveReactGridLayout
className="layout"
draggableCancel=".drag-ignore"
layouts={{
lg: layouts.map((layout) => layout.layout),
lg: channels.map((channel) => channel.layout),
}}
cols={{ lg: 24, md: 20, sm: 14, xs: 10, xxs: 6 }}
rowHeight={50}
Expand All @@ -252,16 +123,8 @@ export default function Home() {
{children}
</ResponsiveReactGridLayout>
<div className="fixed bottom-10 right-10 z-50 flex gap-2">
<PresetSelector
setChannels={setChannels}
setLayouts={setLayouts}
setData={setData}
/>
<TelemetryAdder
setChannels={setChannels}
setLayouts={setLayouts}
setData={setData}
/>
<PresetSelector setChannels={setChannels} />
<TelemetryAdder setChannels={setChannels} />
</div>
</div>
);
Expand Down
18 changes: 2 additions & 16 deletions ground-server/src/components/dashboard/preset-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,10 @@ import { type Layout } from "react-grid-layout";

export function PresetSelector({
setChannels,
setLayouts,
setData,
}: {
setChannels: Dispatch<
SetStateAction<{ id: string; channel: TelemetryChannel }[]>
SetStateAction<{ id: string; channel: TelemetryChannel, layout: Layout }[]>
>;
setLayouts: Dispatch<SetStateAction<{ id: string; layout: Layout }[]>>;
setData: Dispatch<SetStateAction<{ id: string; data: DataPoint[] }[]>>;
}) {
const [open, setOpen] = useState(false);
const isDesktop = useMediaQuery("(min-width: 768px)");
Expand All @@ -61,8 +57,6 @@ export function PresetSelector({
<PresetList
setOpen={setOpen}
setChannels={setChannels}
setLayouts={setLayouts}
setData={setData}
/>
</PopoverContent>
</Popover>
Expand All @@ -87,8 +81,6 @@ export function PresetSelector({
<PresetList
setOpen={setOpen}
setChannels={setChannels}
setLayouts={setLayouts}
setData={setData}
/>
</div>
</DrawerContent>
Expand All @@ -99,21 +91,15 @@ export function PresetSelector({
function PresetList({
setOpen,
setChannels,
setLayouts,
setData,
}: {
setOpen: (open: boolean) => void;
setChannels: Dispatch<
SetStateAction<{ id: string; channel: TelemetryChannel }[]>
SetStateAction<{ id: string; channel: TelemetryChannel, layout: Layout }[]>
>;
setLayouts: Dispatch<SetStateAction<{ id: string; layout: Layout }[]>>;
setData: Dispatch<SetStateAction<{ id: string; data: DataPoint[] }[]>>;
}) {
const selectPreset = (preset: Preset) => {
console.log("Selected preset:", preset);
setChannels([]);
setLayouts([]);
setData([]);
setOpen(false);
};

Expand Down
Loading

0 comments on commit 3ed93d0

Please sign in to comment.