Skip to content
Merged
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
140 changes: 140 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
"@blueprintjs/icons": "^5.21.0",
"@react-three/drei": "^9.121.4",
"@react-three/fiber": "^8.17.14",
"fetch-cookie": "^3.2.0",
"js-cookie": "^3.0.5",
"leaflet": "^1.9.4",
"next": "15.1.6",
"next-runtime-env": "^3.3.0",
"node-fetch": "^3.3.2",
"ping": "^1.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand All @@ -27,6 +29,7 @@
"recharts": "^2.15.1",
"roslib": "^1.4.1",
"three": "^0.173.0",
"tough-cookie": "^6.0.0",
"uuid": "^13.0.0",
"webrtc-adapter": "^9.0.1"
},
Expand Down
Binary file removed public/marker-icon.png
Binary file not shown.
60 changes: 33 additions & 27 deletions src/app/dashboard/api/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import fetch from 'node-fetch';
import fetchCookie from 'fetch-cookie';
import { CookieJar } from 'tough-cookie';

const jar = new CookieJar();
const fetchWithCookies = fetchCookie(fetch, jar);

const USERNAME = 'ubnt';
const PASSWORD = 'samitherover';
const baseStationIP = '192.168.0.2';

const hosts = ['192.168.0.2', '172.19.228.1']; // Add more hosts here as needed
const hosts = ['192.168.0.2', '192.168.0.3', '192.168.0.55']; // Add more hosts here as needed

const ping = require('ping');

Expand Down Expand Up @@ -34,36 +41,37 @@ async function pingHosts(hosts: string[]): Promise<{ [key: string]: number }> {

// Authenticates with the base station
async function authenticate() {
const response = await fetch(`http://${baseStationIP}/api/auth`, {
const formData = new URLSearchParams();
formData.append('uri', '/index.cgi');
formData.append('username', USERNAME);
formData.append('password', PASSWORD);

const response = await fetchWithCookies(`http://${baseStationIP}/login.cgi`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: USERNAME,
password: PASSWORD,
}),
credentials: 'include',
body: formData,
redirect: 'manual',
});
if (!response.ok) {
throw new Error('Authentication failed');
}
const setCookie = response.headers.getSetCookie()[0]

return {cookie: setCookie}
if (response.status !== 302) {
throw new Error(`Authentication failed: ${response.status}`);
}
}

// Fetches status JSON from the base station
async function fetchStatus(cookie: string) {
const response = await fetch(`http://${baseStationIP}/status.cgi`, {
async function fetchStatus() {
const response = await fetchWithCookies(`http://${baseStationIP}/status.cgi`, {
method: 'GET',
credentials: 'include',
headers: {
'Cookie': cookie
}
redirect: 'manual',
});


if (response.status === 302) {
await authenticate();
throw new Error('Not authenticated (redirected to login)');
}

if (!response.ok) {
throw new Error('Failed to fetch status, error code: ' + response.status);
throw new Error(`Failed to fetch status: ${response.status}`);
}
return response.json();
}
Expand All @@ -78,11 +86,9 @@ export async function GET(request: Request) {

// Try to fetch base station data, but don't fail if it's unavailable
try {
const authStatus = await authenticate();
const status = await fetchStatus(authStatus.cookie);

uplinkCapacity = status.wireless?.polling?.ucap ?? 0;
downlinkCapacity = status.wireless?.polling?.dcap ?? 0;
const status : any = await fetchStatus();
uplinkCapacity = status.wireless?.txrate ?? 0;
downlinkCapacity = status.wireless?.rxrate ?? 0;
uplinkThroughput = status.wireless?.throughput?.tx ?? 0;
downlinkThroughput = status.wireless?.throughput?.rx ?? 0;
} catch (error: any) {
Expand Down
17 changes: 0 additions & 17 deletions src/components/BreadCrumbTrail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,13 @@ interface Breadcrumb {
timestamp: number;
}

const breadcrumbIcon = L.icon({
iconUrl: '../../public/marker-icon.png',
iconSize: [10, 10],
iconAnchor: [5, 5],
popupAnchor: [0, -5],
});

const BreadcrumbTrail: React.FC = () => {
const { ros, connectionStatus } = useROS();
const { addWaypoint } = useWaypoints();
const [breadcrumbs, setBreadcrumbs] = useState<Breadcrumb[]>([]);
const [paused, setPaused] = useState<boolean>(false);
const [lastFix, setLastFix] = useState<Breadcrumb | null>(null);

// i hate react
useEffect(() => {
if (!ros) return;

Expand Down Expand Up @@ -104,15 +96,6 @@ const BreadcrumbTrail: React.FC = () => {
color="yellow"
/>
)}
{breadcrumbs.map((breadcrumb, index) => (
<Marker key={index} position={breadcrumb.coordinate} icon={breadcrumbIcon}>
<Popup>
<div style={{ fontSize: '0.85rem' }}>
Recorded at: {new Date(breadcrumb.timestamp).toLocaleTimeString()}
</div>
</Popup>
</Marker>
))}

<div
style={{
Expand Down
11 changes: 2 additions & 9 deletions src/components/SrtStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ const formatNumber = (ms: number | null | undefined) => {
return ms.toLocaleString();
};

const formatSecondsMs = (sec: number | null | undefined) => {
if (sec === null || sec === undefined) return "—";
const ms = sec * 1000.0;
if (!Number.isFinite(ms)) return "—";
return ms >= 10 ? `${ms.toFixed(0)} ms` : `${ms.toFixed(1)} ms`;
};

const formatBandwidth = (bps: number | null | undefined) => {
if (bps === null || bps === undefined) return "—";
if (!Number.isFinite(bps)) return "—";
Expand Down Expand Up @@ -55,7 +48,7 @@ const SrtStats: React.FC = () => {

const topic = new ROSLIB.Topic({
ros,
name: "/srt_node/stats",
name: "/srt_node/srt_stats",
messageType: "interfaces/msg/SrtStats",
});

Expand Down Expand Up @@ -135,7 +128,7 @@ const SrtStats: React.FC = () => {
>
<div style={{ display: "flex", justifyContent: "space-between", gap: "0.5rem" }}>
<span style={{ color: "#aaa" }}>RTT</span>
<span style={{ color: "#f1f1f1" }}>{formatSecondsMs(stats?.rtt)}</span>
<span style={{ color: "#f1f1f1" }}>{formatNumber(stats?.rtt)}</span>
</div>

<div style={{ display: "flex", justifyContent: "space-between", gap: "0.5rem" }}>
Expand Down
Loading
Loading