Packet formatting changes

This commit is contained in:
Daniel Pupius
2025-04-22 21:13:30 -07:00
parent f4504c4cd5
commit 13187eb520
7 changed files with 64 additions and 51 deletions
+15 -43
View File
@@ -16,9 +16,6 @@ export const PacketList: React.FC = () => {
const dispatch = useAppDispatch();
const [currentPage, setCurrentPage] = useState(1);
// A unique ID is attached to each rendered packet element
const [packetKeys, setPacketKeys] = useState<Record<string, string>>({});
// Generate a reproducible hash code for a string
const hashString = useCallback((str: string): string => {
let hash = 0;
@@ -31,42 +28,21 @@ export const PacketList: React.FC = () => {
return Math.abs(hash).toString(36);
}, []);
// Create a consistent, unique fingerprint for a packet
const createPacketFingerprint = useCallback((packet: Packet): string => {
// Combine multiple fields to maximize uniqueness
const parts = [
packet.data.from?.toString() ?? 'unknown',
packet.data.id?.toString() ?? 'noid',
packet.data.portNum?.toString() ?? 'noport',
packet.info.channel ?? 'nochannel',
packet.info.userId ?? 'nouser',
// Include a hash of raw JSON data for extra uniqueness
hashString(JSON.stringify(packet)),
];
return parts.join('_');
// Create a packet key using data.id and from address
// This should match the key generation logic in the reducer
const createPacketKey = useCallback((packet: Packet): string => {
if (packet.data.id !== undefined && packet.data.from !== undefined) {
// Use Meshtastic node ID format (! followed by lowercase hex) and packet ID
const nodeId = `!${packet.data.from.toString(16).toLowerCase()}`;
return `${nodeId}_${packet.data.id}`;
} else {
// Fallback to hash-based key if no ID or from (should be rare)
return `hash_${hashString(JSON.stringify(packet))}`;
}
}, [hashString]);
// Update the packet keys whenever the packets change
useEffect(() => {
// Store new packet keys
const newKeys: Record<string, string> = {...packetKeys};
// Assign keys to any packets that don't already have them
packets.forEach((packet) => {
const fingerprint = createPacketFingerprint(packet);
// Only create new keys for packets we haven't seen before
if (!newKeys[fingerprint]) {
const randomPart = Math.random().toString(36).substring(2, 10);
newKeys[fingerprint] = `${fingerprint}_${randomPart}`;
}
});
// Only update state if we actually added new keys
if (Object.keys(newKeys).length !== Object.keys(packetKeys).length) {
setPacketKeys(newKeys);
}
}, [packets, createPacketFingerprint]);
// We don't need to track packet keys in state anymore since we use data.id
// and it's deterministic - removing this effect to prevent the infinite loop issue
if (loading) {
return (
@@ -103,8 +79,6 @@ export const PacketList: React.FC = () => {
if (window.confirm("Are you sure you want to clear all packets?")) {
dispatch(clearPackets());
setCurrentPage(1);
// Clear the packet keys too
setPacketKeys({});
}
};
@@ -116,11 +90,9 @@ export const PacketList: React.FC = () => {
}
};
// Get the key for a packet
// Get the key for a packet - directly use createPacketKey
const getPacketKey = (packet: Packet, index: number): string => {
const fingerprint = createPacketFingerprint(packet);
// Fallback to index-based key if no fingerprint key exists
return packetKeys[fingerprint] || `packet_${index}_${Date.now()}`;
return createPacketKey(packet) || `fallback_${index}`;
};
return (
+1 -1
View File
@@ -46,7 +46,7 @@ export const GenericPacket: React.FC<GenericPacketProps> = ({ packet }) => {
<Package className="h-4 w-4 text-neutral-100" />
</div>
<span className="font-medium text-neutral-200">
From: {data.from || "Unknown"}
From: {data.from ? `!${data.from.toString(16).toLowerCase()}` : "Unknown"}
</span>
</div>
<span className="text-neutral-400 text-sm">
@@ -22,7 +22,7 @@ export const NodeInfoPacket: React.FC<NodeInfoPacketProps> = ({ packet }) => {
<User className="h-4 w-4 text-neutral-100" />
</div>
<span className="font-medium text-neutral-200">
From: {data.from || "Unknown"}
From: {data.from ? `!${data.from.toString(16).toLowerCase()}` : "Unknown"}
</span>
</div>
<span className="text-neutral-400 text-sm">
@@ -26,7 +26,7 @@ export const PositionPacket: React.FC<PositionPacketProps> = ({ packet }) => {
<MapPin className="h-4 w-4 text-neutral-100" />
</div>
<span className="font-medium text-neutral-200">
From: {data.from || "Unknown"}
From: {data.from ? `!${data.from.toString(16).toLowerCase()}` : "Unknown"}
</span>
</div>
<span className="text-neutral-400 text-sm">
@@ -49,7 +49,7 @@ export const TelemetryPacket: React.FC<TelemetryPacketProps> = ({ packet }) => {
<BarChart className="h-4 w-4 text-neutral-100" />
</div>
<span className="font-medium text-neutral-200">
From: {data.from || "Unknown"}
From: {data.from ? `!${data.from.toString(16).toLowerCase()}` : "Unknown"}
</span>
</div>
<span className="text-neutral-400 text-sm">
@@ -17,7 +17,7 @@ export const TextMessagePacket: React.FC<TextMessagePacketProps> = ({ packet })
<MessageSquareText className="h-4 w-4 text-neutral-100" />
</div>
<span className="font-medium text-neutral-200">
From: {data.from || "Unknown"}
From: {data.from ? `!${data.from.toString(16).toLowerCase()}` : "Unknown"}
</span>
</div>
<span className="text-neutral-400 text-sm">
+44 -3
View File
@@ -10,6 +10,7 @@ interface PacketState {
error: string | null;
streamPaused: boolean;
bufferedPackets: Packet[]; // Holds packets received while paused
seenPackets: Record<string, boolean>; // Tracks already seen packet IDs to prevent duplicates
}
const initialState: PacketState = {
@@ -18,6 +19,7 @@ const initialState: PacketState = {
error: null,
streamPaused: false,
bufferedPackets: [],
seenPackets: {}, // Empty object to track seen packets
};
const packetSlice = createSlice({
@@ -29,8 +31,26 @@ const packetSlice = createSlice({
state.error = null;
},
fetchPacketsSuccess(state, action: PayloadAction<Packet[]>) {
// Track unique packets and update the seen packets object
const uniquePackets: Packet[] = [];
action.payload.forEach(packet => {
if (packet.data.from !== undefined && packet.data.id !== undefined) {
const nodeId = `!${packet.data.from.toString(16).toLowerCase()}`;
const packetKey = `${nodeId}_${packet.data.id}`;
if (!state.seenPackets[packetKey]) {
state.seenPackets[packetKey] = true;
uniquePackets.push(packet);
}
} else {
// If we don't have from or id (rare), just add the packet
uniquePackets.push(packet);
}
});
// Limit initial load to MAX_PACKETS
state.packets = action.payload.slice(-MAX_PACKETS);
state.packets = uniquePackets.slice(-MAX_PACKETS);
state.loading = false;
},
fetchPacketsFailure(state, action: PayloadAction<string>) {
@@ -38,9 +58,29 @@ const packetSlice = createSlice({
state.loading = false;
},
addPacket(state, action: PayloadAction<Packet>) {
const packet = action.payload;
// Skip packets without valid from/id
if (packet.data.from === undefined || packet.data.id === undefined) {
return;
}
// Create a Meshtastic node ID format
const nodeId = `!${packet.data.from.toString(16).toLowerCase()}`;
const packetKey = `${nodeId}_${packet.data.id}`;
// Check if we've already seen this packet
if (state.seenPackets[packetKey]) {
// Packet is a duplicate, ignore it
return;
}
// Mark this packet as seen
state.seenPackets[packetKey] = true;
if (state.streamPaused) {
// When paused, add to buffer instead of main list
state.bufferedPackets.unshift(action.payload);
state.bufferedPackets.unshift(packet);
// Ensure buffer doesn't grow too large
if (state.bufferedPackets.length > MAX_PACKETS) {
@@ -48,7 +88,7 @@ const packetSlice = createSlice({
}
} else {
// Normal flow - add to main list
state.packets.unshift(action.payload);
state.packets.unshift(packet);
// Remove oldest packets if we exceed the limit
if (state.packets.length > MAX_PACKETS) {
@@ -59,6 +99,7 @@ const packetSlice = createSlice({
clearPackets(state) {
state.packets = [];
state.bufferedPackets = [];
state.seenPackets = {}; // Reset the seen packets object
},
toggleStreamPause(state) {
state.streamPaused = !state.streamPaused;