From 7e384c12bbca1a173ac0ddbb4cb50b1ab25bec1d Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Mon, 9 Mar 2026 09:23:35 -0700 Subject: [PATCH] Fix trace packet handling (closes #44) --- frontend/src/components/RawPacketList.tsx | 7 ++++++- frontend/src/test/visualizerUtils.test.ts | 12 ++++++++++++ frontend/src/utils/visualizerUtils.ts | 8 +++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/RawPacketList.tsx b/frontend/src/components/RawPacketList.tsx index 6e206a8..3c79384 100644 --- a/frontend/src/components/RawPacketList.tsx +++ b/frontend/src/components/RawPacketList.tsx @@ -49,9 +49,14 @@ function decodePacketSummary( const routeType = Utils.getRouteTypeName(decoded.routeType); const payloadTypeName = Utils.getPayloadTypeName(decoded.payloadType); + const tracePayload = + decoded.payloadType === PayloadType.Trace && decoded.payload.decoded + ? (decoded.payload.decoded as { pathHashes?: string[] }) + : null; + const pathTokens = tracePayload?.pathHashes || decoded.path || []; // Build path string if available - const pathStr = decoded.path && decoded.path.length > 0 ? ` via ${decoded.path.join('-')}` : ''; + const pathStr = pathTokens.length > 0 ? ` via ${pathTokens.join('-')}` : ''; // Generate summary based on payload type let summary = payloadTypeName; diff --git a/frontend/src/test/visualizerUtils.test.ts b/frontend/src/test/visualizerUtils.test.ts index 6bcd9bc..3825e0c 100644 --- a/frontend/src/test/visualizerUtils.test.ts +++ b/frontend/src/test/visualizerUtils.test.ts @@ -4,9 +4,11 @@ import { analyzeRepeaterTraffic, buildAmbiguousRepeaterLabel, buildAmbiguousRepeaterNodeId, + parsePacket, recordTrafficObservation, type RepeaterTrafficData, } from '../utils/visualizerUtils'; +import { PayloadType } from '@michaelhart/meshcore-decoder'; describe('visualizer multibyte hop identity helpers', () => { it('preserves the full hop token in ambiguous node ids', () => { @@ -49,3 +51,13 @@ describe('visualizer traffic pattern grouping', () => { expect(second.shouldSplit).toBe(false); }); }); + +describe('visualizer packet parsing', () => { + it('uses trace payload hashes instead of outer SNR bytes for TRACE packets', () => { + const parsed = parsePacket('260233277e17b0f300000000007df6'); + + expect(parsed).not.toBeNull(); + expect(parsed?.payloadType).toBe(PayloadType.Trace); + expect(parsed?.pathBytes).toEqual(['7D', 'F6']); + }); +}); diff --git a/frontend/src/utils/visualizerUtils.ts b/frontend/src/utils/visualizerUtils.ts index d0a8263..cbc69de 100644 --- a/frontend/src/utils/visualizerUtils.ts +++ b/frontend/src/utils/visualizerUtils.ts @@ -143,11 +143,17 @@ export function parsePacket(hexData: string): ParsedPacket | null { try { const decoded = MeshCoreDecoder.decode(hexData); if (!decoded.isValid) return null; + const tracePayload = + decoded.payloadType === PayloadType.Trace && decoded.payload.decoded + ? (decoded.payload.decoded as { pathHashes?: string[] }) + : null; const result: ParsedPacket = { payloadType: decoded.payloadType, messageHash: decoded.messageHash || null, - pathBytes: decoded.path || [], + // TRACE reuses the outer packet path field for SNR samples, not hop identities. + // For visualization, use the trace payload's actual node hashes instead. + pathBytes: tracePayload?.pathHashes || decoded.path || [], srcHash: null, dstHash: null, advertPubkey: null,