mirror of
https://github.com/gadgethd/ukmesh.git
synced 2026-09-16 12:02:35 +00:00
improve link visibility and network-scoped viable pairs
This commit is contained in:
+13
-4
@@ -155,11 +155,20 @@ export async function getLastNPackets(n: number, network?: string) {
|
||||
export const MIN_LINK_OBSERVATIONS = 5;
|
||||
|
||||
/** Returns only confirmed viable link pairs — compact for sending in initial WebSocket state. */
|
||||
export async function getViableLinkPairs(): Promise<[string, string][]> {
|
||||
export async function getViableLinkPairs(network?: string): Promise<[string, string][]> {
|
||||
const params: unknown[] = [MIN_LINK_OBSERVATIONS];
|
||||
const networkFilter = network ? 'AND a.network = $2 AND b.network = $2' : '';
|
||||
if (network) params.push(network);
|
||||
|
||||
const res = await pool.query<{ node_a_id: string; node_b_id: string }>(
|
||||
`SELECT node_a_id, node_b_id FROM node_links
|
||||
WHERE (itm_viable = true OR force_viable = true) AND observed_count >= $1`,
|
||||
[MIN_LINK_OBSERVATIONS],
|
||||
`SELECT nl.node_a_id, nl.node_b_id
|
||||
FROM node_links nl
|
||||
JOIN nodes a ON a.node_id = nl.node_a_id
|
||||
JOIN nodes b ON b.node_id = nl.node_b_id
|
||||
WHERE (nl.itm_viable = true OR nl.force_viable = true)
|
||||
AND nl.observed_count >= $1
|
||||
${networkFilter}`,
|
||||
params,
|
||||
);
|
||||
return res.rows.map((r) => [r.node_a_id, r.node_b_id]);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function initWebSocketServer(httpServer: Server): WebSocketServer {
|
||||
// Send initial state: known nodes + last 5 minutes of packets
|
||||
try {
|
||||
const [nodes, packets, viablePairs] = await Promise.all([
|
||||
getNodes(network), getLastNPackets(10, network), getViableLinkPairs(),
|
||||
getNodes(network), getLastNPackets(10, network), getViableLinkPairs(network),
|
||||
]);
|
||||
const initMsg: WSMessage = {
|
||||
type: 'initial_state',
|
||||
|
||||
@@ -137,6 +137,7 @@ export const App: React.FC = () => {
|
||||
showClientNodes={filters.clientNodes}
|
||||
showLinks={filters.links}
|
||||
viablePairsArr={viablePairsArr}
|
||||
linkMetrics={linkMetrics}
|
||||
packetPath={packetPath}
|
||||
betaPath={betaPacketPath}
|
||||
showBetaPaths={filters.betaPaths || pinnedPacketId !== null}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { MapContainer, TileLayer, useMap, Pane, Polygon, Polyline } from 'react-
|
||||
import type { LatLngExpression, Map as LeafletMap, Polyline as LeafletPolyline } from 'leaflet';
|
||||
import type { MeshNode, PacketArc } from '../../hooks/useNodes.js';
|
||||
import type { NodeCoverage } from '../../hooks/useCoverage.js';
|
||||
import type { LinkMetrics } from '../../utils/pathing.js';
|
||||
import { NodeMarker } from './NodeMarker.js';
|
||||
import { PacketArcLayer } from './PacketArcLayer.js';
|
||||
import { NodeSearch } from './NodeSearch.js';
|
||||
@@ -88,6 +89,7 @@ interface MapViewProps {
|
||||
showClientNodes: boolean;
|
||||
showLinks: boolean;
|
||||
viablePairsArr: [string, string][];
|
||||
linkMetrics: Map<string, LinkMetrics>;
|
||||
packetPath: [number, number][] | null;
|
||||
betaPath: [number, number][] | null;
|
||||
showBetaPaths: boolean;
|
||||
@@ -101,7 +103,7 @@ const DEFAULT_ZOOM = 11;
|
||||
|
||||
export const MapView: React.FC<MapViewProps> = ({
|
||||
nodes, arcs, activeNodes, coverage, showPackets, showCoverage, showClientNodes,
|
||||
showLinks, viablePairsArr, packetPath, betaPath, showBetaPaths, pathOpacity, onMapReady,
|
||||
showLinks, viablePairsArr, linkMetrics, packetPath, betaPath, showBetaPaths, pathOpacity, onMapReady,
|
||||
}) => {
|
||||
const [map, setMap] = useState<LeafletMap | null>(null);
|
||||
|
||||
@@ -181,19 +183,49 @@ export const MapView: React.FC<MapViewProps> = ({
|
||||
return m;
|
||||
}, [coverage]);
|
||||
|
||||
const linkKey = (a: string, b: string) => (a < b ? `${a}:${b}` : `${b}:${a}`);
|
||||
|
||||
const linkColor = (pathLossDb: number | null | undefined) => {
|
||||
if (pathLossDb == null) return '#fbbf24';
|
||||
if (pathLossDb <= 120) return '#22c55e';
|
||||
if (pathLossDb <= 135) return '#fbbf24';
|
||||
return '#ef4444';
|
||||
};
|
||||
|
||||
// Resolve viable link pairs to lat/lon polyline positions
|
||||
const linkLines = useMemo(() => {
|
||||
if (!showLinks || viablePairsArr.length === 0) return [];
|
||||
const lines: [number, number][][] = [];
|
||||
const lines: Array<{
|
||||
key: string;
|
||||
positions: [number, number][];
|
||||
observedCount: number;
|
||||
pathLossDb: number | null | undefined;
|
||||
}> = [];
|
||||
for (const [aId, bId] of viablePairsArr) {
|
||||
const a = nodes.get(aId);
|
||||
const b = nodes.get(bId);
|
||||
if (hasCoords(a) && hasCoords(b)) {
|
||||
lines.push([[a.lat, a.lon], [b.lat, b.lon]]);
|
||||
if (
|
||||
hasCoords(a)
|
||||
&& hasCoords(b)
|
||||
&& (Date.now() - new Date(a.last_seen).getTime()) < FOURTEEN_DAYS_MS
|
||||
&& (Date.now() - new Date(b.last_seen).getTime()) < FOURTEEN_DAYS_MS
|
||||
&& !a.name?.includes('🚫')
|
||||
&& !b.name?.includes('🚫')
|
||||
&& (a.role === undefined || a.role === 2)
|
||||
&& (b.role === undefined || b.role === 2)
|
||||
) {
|
||||
const key = linkKey(aId, bId);
|
||||
const metrics = linkMetrics.get(key);
|
||||
lines.push({
|
||||
key,
|
||||
positions: [[a.lat, a.lon], [b.lat, b.lon]],
|
||||
observedCount: metrics?.observed_count ?? 0,
|
||||
pathLossDb: metrics?.itm_path_loss_db,
|
||||
});
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}, [showLinks, viablePairsArr, nodes]);
|
||||
}, [showLinks, viablePairsArr, nodes, linkMetrics]);
|
||||
|
||||
return (
|
||||
<div className="map-area">
|
||||
@@ -240,18 +272,24 @@ export const MapView: React.FC<MapViewProps> = ({
|
||||
{/* Confirmed link lines — ITM-viable node pairs */}
|
||||
{showLinks && linkLines.length > 0 && (
|
||||
<Pane name="linksPane" style={{ zIndex: 400 }}>
|
||||
{linkLines.map((positions, i) => (
|
||||
{linkLines.map((line) => {
|
||||
const obs = Math.max(1, line.observedCount);
|
||||
const strength = Math.log10(obs + 1);
|
||||
const opacity = Math.min(0.85, 0.35 + strength * 0.22);
|
||||
const weight = Math.min(3.2, 1.0 + strength * 1.1);
|
||||
return (
|
||||
<Polyline
|
||||
key={i}
|
||||
positions={positions}
|
||||
key={line.key}
|
||||
positions={line.positions}
|
||||
pathOptions={{
|
||||
color: '#fbbf24',
|
||||
weight: 1.5,
|
||||
opacity: 0.55,
|
||||
color: linkColor(line.pathLossDb),
|
||||
weight,
|
||||
opacity,
|
||||
}}
|
||||
interactive={false}
|
||||
/>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</Pane>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user